From 320e8380fba04789b11ba71c09d2f49842527dfd Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:36 +0100 Subject: [PATCH 001/521] UPSTREAM: drm/imagination: Count paired job fence as dependency in prepare_job() The DRM scheduler's prepare_job() callback counts the remaining non-signaled native dependencies for a job, preventing job submission until those (plus job data and fence update) can fit in the job queue's CCCB. This means checking which dependencies can be waited upon in the firmware, i.e. whether they are backed by a UFO object, i.e. whether their drm_sched_fence::parent has been assigned to a pvr_queue_fence::base fence. That happens when the job owning the fence is submitted to the firmware. Paired geometry and fragment jobs are submitted at the same time, which means the dependency between them can't be checked this way before submission. Update job_count_remaining_native_deps() to take into account the dependency between paired jobs. This fixes cases where prepare_job() underestimated the space left in an almost full fragment CCCB, wrongly unblocking run_job(), which then returned early without writing the full sequence of commands to the CCCB. The above lead to kernel warnings such as the following and potentially job timeouts (depending on waiters on the missing commands): [ 375.702979] WARNING: drivers/gpu/drm/imagination/pvr_cccb.c:178 at pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr], CPU#1: kworker/u16:3/47 [ 375.703160] Modules linked in: [ 375.703571] CPU: 1 UID: 0 PID: 47 Comm: kworker/u16:3 Tainted: G W 7.0.0-rc2-g817eb6b11ad5 #40 PREEMPT [ 375.703613] Tainted: [W]=WARN [ 375.703627] Hardware name: Texas Instruments AM625 SK (DT) [ 375.703645] Workqueue: powervr-sched drm_sched_run_job_work [gpu_sched] [ 375.703741] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 375.703764] pc : pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] [ 375.703847] lr : pvr_queue_submit_job_to_cccb+0x578/0xa70 [powervr] [ 375.703921] sp : ffff800084a97650 [ 375.703934] x29: ffff800084a97740 x28: 0000000000000958 x27: ffff80008565d000 [ 375.703979] x26: 0000000000000030 x25: ffff800084a97680 x24: 0000000000001000 [ 375.704017] x23: ffff800084a97820 x22: 1ffff00010952ecc x21: 0000000000000008 [ 375.704056] x20: 00000000000006a8 x19: ffff00002ff7da88 x18: 0000000000000000 [ 375.704093] x17: 0000000020020000 x16: 0000000000020000 x15: 0000000000000000 [ 375.704132] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 375.704168] x11: 000000000000f2f2 x10: 00000000f3000000 x9 : 00000000f3f3f3f3 [ 375.704206] x8 : 00000000f2f2f200 x7 : ffff700010952ecc x6 : 0000000000000008 [ 375.704243] x5 : 0000000000000000 x4 : 1ffff00010acba00 x3 : 0000000000000000 [ 375.704279] x2 : 0000000000000007 x1 : 0000000000000fff x0 : 000000000000002f [ 375.704317] Call trace: [ 375.704331] pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] (P) [ 375.704411] pvr_queue_submit_job_to_cccb+0x578/0xa70 [powervr] [ 375.704487] pvr_queue_run_job+0x3a4/0x990 [powervr] [ 375.704562] drm_sched_run_job_work+0x580/0xd48 [gpu_sched] [ 375.704623] process_one_work+0x520/0x1288 [ 375.704658] worker_thread+0x3f0/0xb3c [ 375.704680] kthread+0x334/0x3d8 [ 375.704706] ret_from_fork+0x10/0x20 [ 375.704736] ---[ end trace 0000000000000000 ]--- Fixes: eaf01ee5ba28 ("drm/imagination: Implement job submission and scheduling") Cc: stable@vger.kernel.org Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-1-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit 9cd74f935306cd857f46686975c43383e1d95f94) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_queue.c | 27 +++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index dd88949f61944c..836feaa0b295db 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -179,7 +179,7 @@ static const struct dma_fence_ops pvr_queue_job_fence_ops = { /** * to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is - * backed by a UFO. + * already backed by a UFO. * @f: The dma_fence to turn into a pvr_queue_fence. * * Return: @@ -356,6 +356,15 @@ static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count) pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len); } +static bool +is_paired_job_fence(struct dma_fence *fence, struct pvr_job *job) +{ + /* This assumes "fence" is one of "job"'s drm_sched_job::dependencies */ + return job->type == DRM_PVR_JOB_TYPE_FRAGMENT && + job->paired_job && + &job->paired_job->base.s_fence->scheduled == fence; +} + /** * job_count_remaining_native_deps() - Count the number of non-signaled native dependencies. * @job: Job to operate on. @@ -371,6 +380,17 @@ static unsigned long job_count_remaining_native_deps(struct pvr_job *job) xa_for_each(&job->base.dependencies, index, fence) { struct pvr_queue_fence *jfence; + if (is_paired_job_fence(fence, job)) { + /* + * A fence between paired jobs won't resolve to a pvr_queue_fence (i.e. + * be backed by a UFO) until the jobs have been submitted, together. + * The submitting code will insert a partial render fence command for this. + */ + WARN_ON(dma_fence_is_signaled(fence)); + remaining_count++; + continue; + } + jfence = to_pvr_queue_job_fence(fence); if (!jfence) continue; @@ -630,9 +650,8 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) if (!jfence) continue; - /* Skip the partial render fence, we will place it at the end. */ - if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job && - &job->paired_job->base.s_fence->scheduled == fence) + /* This fence will be placed last, as partial render fence. */ + if (is_paired_job_fence(fence, job)) continue; if (dma_fence_is_signaled(&jfence->base)) From c11db457642c4586517f9d89cd2453522b5ca867 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:37 +0100 Subject: [PATCH 002/521] UPSTREAM: drm/imagination: Fit paired fragment job in the correct CCCB For geometry jobs with a paired fragment job, at the moment, the DRM scheduler's prepare_job() callback: - checks for internal (driver) dependencies for the geometry job; - calls into pvr_queue_get_paired_frag_job_dep() to check for external dependencies for the fragment job (the two jobs are submitted together but the common scheduler code doesn't know about it, so this needs to be done at this point in time); - calls into the prepare_job() callback again, but for the fragment job, to check its internal dependencies as well, passing the fragment job's drm_sched_job and the geometry job's drm_sched_entity / pvr_queue. The problem with the last step is that pvr_queue_prepare_job() doesn't always take the mismatched fragment job and geometry queue into account, in particular when checking whether there is space for the fragment command to be submitted, so the code ends up checking for space in the geometry (i.e. wrong) CCCB. The rest of the nested prepare_job() callback happens to work fine at the moment as the other internal dependencies are not relevant for a paired fragment job. Move the initialisation of a paired fragment job's done fence and CCCB fence to pvr_queue_get_paired_frag_job_dep(), inferring the correct queue from the fragment job itself. This fixes cases where prepare_job() wrongly assumed that there was enough space for a paired fragment job in its own CCCB, unblocking run_job(), which then returned early without writing the full sequence of commands to the CCCB. The above lead to kernel warnings such as the following and potentially job timeouts (depending on waiters on the missing commands): [ 552.421075] WARNING: drivers/gpu/drm/imagination/pvr_cccb.c:178 at pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr], CPU#2: kworker/u16:5/63 [ 552.421230] Modules linked in: [ 552.421592] CPU: 2 UID: 0 PID: 63 Comm: kworker/u16:5 Tainted: G W 7.0.0-rc2-gc5d053e4dccb #39 PREEMPT [ 552.421625] Tainted: [W]=WARN [ 552.421637] Hardware name: Texas Instruments AM625 SK (DT) [ 552.421655] Workqueue: powervr-sched drm_sched_run_job_work [gpu_sched] [ 552.421744] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 552.421766] pc : pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] [ 552.421850] lr : pvr_queue_submit_job_to_cccb+0x57c/0xa74 [powervr] [ 552.421923] sp : ffff800084c47650 [ 552.421936] x29: ffff800084c47740 x28: 0000000000000df8 x27: ffff800088a77000 [ 552.421979] x26: 0000000000000030 x25: ffff800084c47680 x24: 0000000000001000 [ 552.422017] x23: ffff800084c47820 x22: 1ffff00010988ecc x21: 0000000000000008 [ 552.422055] x20: 0000000000000208 x19: ffff000006ad5a88 x18: 0000000000000000 [ 552.422093] x17: 0000000020020000 x16: 0000000000020000 x15: 0000000000000000 [ 552.422130] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 552.422167] x11: 000000000000f2f2 x10: 00000000f3000000 x9 : 00000000f3f3f3f3 [ 552.422204] x8 : 00000000f2f2f200 x7 : ffff700010988ecc x6 : 0000000000000008 [ 552.422241] x5 : 0000000000000000 x4 : 1ffff0001114ee00 x3 : 0000000000000000 [ 552.422278] x2 : 0000000000000007 x1 : 0000000000000fff x0 : 000000000000002f [ 552.422316] Call trace: [ 552.422330] pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] (P) [ 552.422411] pvr_queue_submit_job_to_cccb+0x57c/0xa74 [powervr] [ 552.422486] pvr_queue_run_job+0x3a4/0x990 [powervr] [ 552.422562] drm_sched_run_job_work+0x580/0xd48 [gpu_sched] [ 552.422623] process_one_work+0x520/0x1288 [ 552.422657] worker_thread+0x3f0/0xb3c [ 552.422679] kthread+0x334/0x3d8 [ 552.422706] ret_from_fork+0x10/0x20 Fixes: eaf01ee5ba28 ("drm/imagination: Implement job submission and scheduling") Cc: stable@vger.kernel.org Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-2-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit 4baf9e70cb756d78dd56419f8baee2978a72d0c3) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_queue.c | 32 +++++++++++-------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 836feaa0b295db..f1e54e6d940dd5 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -488,10 +488,11 @@ pvr_queue_get_job_kccb_fence(struct pvr_queue *queue, struct pvr_job *job) } static struct dma_fence * -pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job) +pvr_queue_get_paired_frag_job_dep(struct pvr_job *job) { struct pvr_job *frag_job = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ? job->paired_job : NULL; + struct pvr_queue *frag_queue = frag_job ? frag_job->ctx->queues.fragment : NULL; struct dma_fence *f; unsigned long index; @@ -510,7 +511,10 @@ pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job) return dma_fence_get(f); } - return frag_job->base.sched->ops->prepare_job(&frag_job->base, &queue->entity); + /* Initialize the paired fragment job's done_fence, so we can signal it. */ + pvr_queue_job_fence_init(frag_job->done_fence, frag_queue); + + return pvr_queue_get_job_cccb_fence(frag_queue, frag_job); } /** @@ -529,11 +533,6 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job, struct pvr_queue *queue = container_of(s_entity, struct pvr_queue, entity); struct dma_fence *internal_dep = NULL; - /* - * Initialize the done_fence, so we can signal it. This must be done - * here because otherwise by the time of run_job() the job will end up - * in the pending list without a valid fence. - */ if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) { /* * This will be called on a paired fragment job after being @@ -543,18 +542,15 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job, */ if (job->paired_job->has_pm_ref) return NULL; - - /* - * In this case we need to use the job's own ctx to initialise - * the done_fence. The other steps are done in the ctx of the - * paired geometry job. - */ - pvr_queue_job_fence_init(job->done_fence, - job->ctx->queues.fragment); - } else { - pvr_queue_job_fence_init(job->done_fence, queue); } + /* + * Initialize the done_fence, so we can signal it. This must be done + * here because otherwise by the time of run_job() the job will end up + * in the pending list without a valid fence. + */ + pvr_queue_job_fence_init(job->done_fence, queue); + /* CCCB fence is used to make sure we have enough space in the CCCB to * submit our commands. */ @@ -575,7 +571,7 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job, /* The paired job fence should come last, when everything else is ready. */ if (!internal_dep) - internal_dep = pvr_queue_get_paired_frag_job_dep(queue, job); + internal_dep = pvr_queue_get_paired_frag_job_dep(job); return internal_dep; } From 0decdf90d1a5219a0534d49fce2a4afc7d89d96b Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:38 +0100 Subject: [PATCH 003/521] UPSTREAM: drm/imagination: Skip check on paired job fence during job submission While submitting a paired fragment job, there is no need to manually look for, and skip, the paired job fence, as the existing logic to resolve dependencies to pvr_queue_fence objects will have failed to resolve it already and continued with the next one. Point this out where the fence is actually accessed and drop the related check. Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-3-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit 18998b3cb7595850b8b2da55adb3fdc7aef8bc22) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_queue.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index f1e54e6d940dd5..527eae1309d86e 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -646,10 +646,6 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) if (!jfence) continue; - /* This fence will be placed last, as partial render fence. */ - if (is_paired_job_fence(fence, job)) - continue; - if (dma_fence_is_signaled(&jfence->base)) continue; @@ -664,8 +660,13 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) } } - /* Partial render fence goes last. */ if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) { + /* + * The loop above will only process dependencies backed by a UFO i.e. with + * a valid parent fence assigned, but the paired job dependency won't have + * one until both jobs have been submitted. Access the parent fence directly + * here instead, submitting it last as partial render fence. + */ jfence = to_pvr_queue_job_fence(job->paired_job->done_fence); if (!WARN_ON(!jfence)) { pvr_fw_object_get_fw_addr(jfence->queue->timeline_ufo.fw_obj, From 48833f233bbb2a4948de216023d357c7f2a3a257 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:39 +0100 Subject: [PATCH 004/521] UPSTREAM: drm/imagination: Rename pvr_queue_fence_is_ufo_backed() to reflect usage This function is only used by the synchronization code to figure out if a fence belongs to this driver. Rename it to pvr_queue_fence_is_native() and update its documentation to reflect its current purpose. Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-4-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit c162e655092de8de2e0f7776d72919dd5e3b84f2) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_queue.c | 14 +++++++------- drivers/gpu/drm/imagination/pvr_queue.h | 2 +- drivers/gpu/drm/imagination/pvr_sync.c | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 527eae1309d86e..df0a110ed96fe3 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -898,16 +898,16 @@ static const struct drm_sched_backend_ops pvr_queue_sched_ops = { }; /** - * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO object + * pvr_queue_fence_is_native() - Check if a dma_fence is native to this driver. * @f: Fence to test. * - * A UFO-backed fence is a fence that can be signaled or waited upon FW-side. - * pvr_job::done_fence objects are backed by the timeline UFO attached to the queue - * they are pushed to, but those fences are not directly exposed to the outside - * world, so we also need to check if the fence we're being passed is a - * drm_sched_fence that was coming from our driver. + * Check if the fence we're being passed is a drm_sched_fence that is coming from this driver. + * + * It may be a UFO-backed fence i.e. a fence that can be signaled or waited upon FW-side, + * such as pvr_job::done_fence objects that are backed by the timeline UFO attached to the queue + * they are pushed to. */ -bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f) +bool pvr_queue_fence_is_native(struct dma_fence *f) { struct drm_sched_fence *sched_fence = f ? to_drm_sched_fence(f) : NULL; diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h index fc1986d73fc88e..4aa72665ce2568 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.h +++ b/drivers/gpu/drm/imagination/pvr_queue.h @@ -141,7 +141,7 @@ struct pvr_queue { u64 callstack_addr; }; -bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f); +bool pvr_queue_fence_is_native(struct dma_fence *f); int pvr_queue_job_init(struct pvr_job *job, u64 drm_client_id); diff --git a/drivers/gpu/drm/imagination/pvr_sync.c b/drivers/gpu/drm/imagination/pvr_sync.c index 3582616ff72208..757a18b1ab8f09 100644 --- a/drivers/gpu/drm/imagination/pvr_sync.c +++ b/drivers/gpu/drm/imagination/pvr_sync.c @@ -211,7 +211,7 @@ pvr_sync_add_dep_to_job(struct drm_sched_job *job, struct dma_fence *f) int err = 0; dma_fence_unwrap_for_each(uf, &iter, f) { - if (pvr_queue_fence_is_ufo_backed(uf)) + if (pvr_queue_fence_is_native(uf)) native_fence_count++; } @@ -227,7 +227,7 @@ pvr_sync_add_dep_to_job(struct drm_sched_job *job, struct dma_fence *f) if (err) continue; - if (pvr_queue_fence_is_ufo_backed(uf)) { + if (pvr_queue_fence_is_native(uf)) { struct drm_sched_fence *s_fence = to_drm_sched_fence(uf); /* If this is a native dependency, we wait for the scheduled fence, From 369dd953424fd29300fc1d444e2c88523e7983b0 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:40 +0100 Subject: [PATCH 005/521] UPSTREAM: drm/imagination: Rename fence returned by pvr_queue_job_arm() Rename from done_fence to finished_fence, both because the function returns a drm_sched_fence's finished fence, and to avoid confusion with the job fence, which is called the same but has a different purpose. Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-5-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit 5dae1a21f1e7128a19c68212422383f700699d01) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_job.c | 8 ++++---- drivers/gpu/drm/imagination/pvr_sync.c | 4 ++-- drivers/gpu/drm/imagination/pvr_sync.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_job.c b/drivers/gpu/drm/imagination/pvr_job.c index 0c2f511a617852..dd9f5df01e0803 100644 --- a/drivers/gpu/drm/imagination/pvr_job.c +++ b/drivers/gpu/drm/imagination/pvr_job.c @@ -326,7 +326,7 @@ prepare_job_syncs(struct pvr_file *pvr_file, struct pvr_job_data *job_data, struct xarray *signal_array) { - struct dma_fence *done_fence; + struct dma_fence *finished_fence; int err = pvr_sync_signal_array_collect_ops(signal_array, from_pvr_file(pvr_file), job_data->sync_op_count, @@ -359,13 +359,13 @@ prepare_job_syncs(struct pvr_file *pvr_file, return err; } - /* We need to arm the job to get the job done fence. */ - done_fence = pvr_queue_job_arm(job_data->job); + /* We need to arm the job to get the job finished fence. */ + finished_fence = pvr_queue_job_arm(job_data->job); err = pvr_sync_signal_array_update_fences(signal_array, job_data->sync_op_count, job_data->sync_ops, - done_fence); + finished_fence); return err; } diff --git a/drivers/gpu/drm/imagination/pvr_sync.c b/drivers/gpu/drm/imagination/pvr_sync.c index 757a18b1ab8f09..936f840a522189 100644 --- a/drivers/gpu/drm/imagination/pvr_sync.c +++ b/drivers/gpu/drm/imagination/pvr_sync.c @@ -160,7 +160,7 @@ int pvr_sync_signal_array_update_fences(struct xarray *array, u32 sync_op_count, const struct drm_pvr_sync_op *sync_ops, - struct dma_fence *done_fence) + struct dma_fence *finished_fence) { for (u32 i = 0; i < sync_op_count; i++) { struct dma_fence *old_fence; @@ -175,7 +175,7 @@ pvr_sync_signal_array_update_fences(struct xarray *array, return -EINVAL; old_fence = sig_sync->fence; - sig_sync->fence = dma_fence_get(done_fence); + sig_sync->fence = dma_fence_get(finished_fence); dma_fence_put(old_fence); if (WARN_ON(!sig_sync->fence)) diff --git a/drivers/gpu/drm/imagination/pvr_sync.h b/drivers/gpu/drm/imagination/pvr_sync.h index db6ccfda104a3a..48501ad2779421 100644 --- a/drivers/gpu/drm/imagination/pvr_sync.h +++ b/drivers/gpu/drm/imagination/pvr_sync.h @@ -70,7 +70,7 @@ int pvr_sync_signal_array_update_fences(struct xarray *array, u32 sync_op_count, const struct drm_pvr_sync_op *sync_ops, - struct dma_fence *done_fence); + struct dma_fence *finished_fence); void pvr_sync_signal_array_push_fences(struct xarray *array); From ef8177a5ac7c6ff978e56ec27bbacb6eb4833b9f Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:41 +0100 Subject: [PATCH 006/521] UPSTREAM: drm/imagination: Move repeated job fence check to its own function This should make the code slightly clearer. Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-6-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit 402562e60c6c1b15ee359ba7ffed907baa886a99) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_queue.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index df0a110ed96fe3..4a49d954562e02 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -177,6 +177,24 @@ static const struct dma_fence_ops pvr_queue_job_fence_ops = { .release = pvr_queue_fence_release, }; +/** + * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO. + * @f: The dma_fence to check. + * + * Return: + * * true if the dma_fence is backed by a UFO, or + * * false otherwise. + */ +static inline bool +pvr_queue_fence_is_ufo_backed(struct dma_fence *f) +{ + /* + * Currently the only dma_fence backed by a UFO object is the job fence, + * e.g. pvr_job::done_fence, wrapped by a pvr_queue_fence object. + */ + return f && f->ops == &pvr_queue_job_fence_ops; +} + /** * to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is * already backed by a UFO. @@ -194,7 +212,7 @@ to_pvr_queue_job_fence(struct dma_fence *f) if (sched_fence) f = sched_fence->parent; - if (f && f->ops == &pvr_queue_job_fence_ops) + if (pvr_queue_fence_is_ufo_backed(f)) return container_of(f, struct pvr_queue_fence, base); return NULL; @@ -915,10 +933,7 @@ bool pvr_queue_fence_is_native(struct dma_fence *f) sched_fence->sched->ops == &pvr_queue_sched_ops) return true; - if (f && f->ops == &pvr_queue_job_fence_ops) - return true; - - return false; + return pvr_queue_fence_is_ufo_backed(f); } /** From b026018e1eb2e01d57d653f91a2389b02420e175 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:42 +0100 Subject: [PATCH 007/521] UPSTREAM: drm/imagination: Update check to skip prepare_job() for fragment jobs By the time prepare_job() is called on a paired fragment job, the paired geometry job might already be finished and its PM reference dropped. Check the fragment job's PM reference instead which is a bit more likely to be still set. This is a very minor optimization. Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-7-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit 5c81eb2970133ad073214eb1e5b0c34a1ae793eb) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_queue.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 4a49d954562e02..303f4d6cc09e87 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -553,12 +553,13 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job, if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) { /* - * This will be called on a paired fragment job after being - * submitted to firmware. We can tell if this is the case and - * bail early from whether run_job() has been called on the - * geometry job, which would issue a pm ref. + * This will be called on a paired fragment job after being submitted + * to the firmware as part of the paired geometry job's submission. + * We can tell if this is the case and bail early from whether run_job() + * has been called on the geometry job, which would issue a pm ref on + * this job as well. */ - if (job->paired_job->has_pm_ref) + if (job->has_pm_ref) return NULL; } From 99234f945b51f42bba051a8c1b40298010171521 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:43 +0100 Subject: [PATCH 008/521] UPSTREAM: drm/imagination: Minor improvements to job submission code documentation Mixed list of clarifications and typo fixes. Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-8-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster (cherry picked from commit 62a36c2da774800bef893bc4bf8922fb9c07c1d0) Signed-off-by: Han Gao --- drivers/gpu/drm/imagination/pvr_queue.c | 38 ++++++++++++------- .../drm/imagination/pvr_rogue_fwif_shared.h | 10 +---- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 303f4d6cc09e87..b5bec656d13cf3 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -200,6 +200,13 @@ pvr_queue_fence_is_ufo_backed(struct dma_fence *f) * already backed by a UFO. * @f: The dma_fence to turn into a pvr_queue_fence. * + * This could be called on: + * - a job fence directly, in which case it simply returns the containing pvr_queue_fence; + * - a drm_sched_fence's scheduled or finished fence, in which case it will first try to follow + * the parent pointer to find the job fence (note that the parent pointer is initialized + * only after the run_job() callback is called on the drm_sched_fence's owning job); + * - any other dma_fence, in which case it will return NULL. + * * Return: * * A non-NULL pvr_queue_fence object if the dma_fence is backed by a UFO, or * * NULL otherwise. @@ -367,11 +374,14 @@ static u32 ufo_cmds_size(u32 elem_count) static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count) { - /* One UFO cmd for the fence signaling, one UFO cmd per native fence native, - * and a command for the job itself. + /* + * One UFO command per native fence this job will be waiting on (unless any are + * signaled by the time the job is submitted), plus a command for the job itself, + * plus one UFO command for the fence signaling. */ - return ufo_cmds_size(1) + ufo_cmds_size(ufo_wait_count) + - pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len); + return ufo_cmds_size(ufo_wait_count) + + pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len) + + ufo_cmds_size(1); } static bool @@ -517,12 +527,16 @@ pvr_queue_get_paired_frag_job_dep(struct pvr_job *job) if (!frag_job) return NULL; + /* Have the geometry job wait on the paired fragment job's dependencies as well. */ xa_for_each(&frag_job->base.dependencies, index, f) { /* Skip already signaled fences. */ if (dma_fence_is_signaled(f)) continue; - /* Skip our own fence. */ + /* + * The paired job fence won't be signaled until both jobs have + * been submitted, so we can't wait on it to schedule them. + */ if (f == &job->base.s_fence->scheduled) continue; @@ -665,6 +679,7 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) if (!jfence) continue; + /* Some dependencies might have been signaled since prepare_job() */ if (dma_fence_is_signaled(&jfence->base)) continue; @@ -714,7 +729,7 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd, job->id, job->id); - /* Signal the job fence. */ + /* Update command to signal the job fence. */ pvr_fw_object_get_fw_addr(queue->timeline_ufo.fw_obj, &ufos[0].addr); ufos[0].value = job->done_fence->seqno; pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_UPDATE, @@ -744,10 +759,8 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job) } /* The only kind of jobs that can be paired are geometry and fragment, and - * we bail out early if we see a fragment job that's paired with a geomtry - * job. - * Paired jobs must also target the same context and point to the same - * HWRT. + * we bail out early if we see a fragment job that's paired with a geometry job. + * Paired jobs must also target the same context and point to the same HWRT. */ if (WARN_ON(job->paired_job && (job->type != DRM_PVR_JOB_TYPE_GEOMETRY || @@ -966,9 +979,8 @@ pvr_queue_signal_done_fences(struct pvr_queue *queue) } /** - * pvr_queue_check_job_waiting_for_cccb_space() - Check if the job waiting for CCCB space - * can be unblocked - * pushed to the CCCB + * pvr_queue_check_job_waiting_for_cccb_space() - Check if a job waiting for CCCB space + * can be unblocked and pushed to the CCCB. * @queue: Queue to check * * If we have a job waiting for CCCB, and this job now fits in the CCCB, we signal diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h index 869d904e3649f9..fe54c1cad7a9c5 100644 --- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h @@ -14,15 +14,7 @@ #define ROGUE_NUM_GEOM_CORES_SIZE 2U -/* - * Maximum number of UFOs in a CCB command. - * The number is based on having 32 sync prims (as originally), plus 32 sync - * checkpoints. - * Once the use of sync prims is no longer supported, we will retain - * the same total (64) as the number of sync checkpoints which may be - * supporting a fence is not visible to the client driver and has to - * allow for the number of different timelines involved in fence merges. - */ +/* Maximum number of UFOs in a CCB command. */ #define ROGUE_FWIF_CCB_CMD_MAX_UFOS (32U + 32U) /* From 7b9385b7aff02bd5b6639684666e7eb593772c8b Mon Sep 17 00:00:00 2001 From: Li Guan Date: Thu, 14 May 2026 02:07:21 +0800 Subject: [PATCH 009/521] UPSTREAM: perf riscv: Fix discarded const qualifier in _get_field() The assignment of strrchr() return values to non-const char * variables triggers a -Werror=discarded-qualifiers warning when building with GCC 14. This happens because in newer glibc versions, strrchr() returns a 'const char *' if the input string is const. Properly declare 'line2' and 'nl' as const char * to match the glibc function signature and ensure type safety. This avoids the need for explicit type casting and aligns with the design pattern of not modifying read-only memory in the perf tool. Reviewed-by: Ian Rogers Signed-off-by: Li Guan Cc: Adrian Hunter Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit 7378b6656aa46fda56f2743d5a7c1f619c2f6f9b) Signed-off-by: Han Gao --- tools/perf/arch/riscv/util/header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/riscv/util/header.c b/tools/perf/arch/riscv/util/header.c index 4b839203d4a54d..891984e909bd8f 100644 --- a/tools/perf/arch/riscv/util/header.c +++ b/tools/perf/arch/riscv/util/header.c @@ -19,7 +19,7 @@ static char *_get_field(const char *line) { - char *line2, *nl; + const char *line2, *nl; line2 = strrchr(line, ' '); if (!line2) From 90a18aaee7d47ac4972e1beb52ba8e392e20bddb Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 3 Nov 2025 00:02:00 +0100 Subject: [PATCH 010/521] UPSTREAM: mfd: simple-mfd-i2c: Add a reboot cell for the SpacemiT P1 chip Add a "spacemit-p1-reboot" cell for the SpacemiT P1 chip. Signed-off-by: Aurelien Jarno Reviewed-by: Troy Mitchell Tested-by: Vincent Legoll # OrangePi-RV2 Link: https://patch.msgid.link/20251102230352.914421-3-aurelien@aurel32.net Signed-off-by: Lee Jones (cherry picked from commit 25c80f08e54ef5298eb68866f755f35b59a26d2b) Signed-off-by: Han Gao --- drivers/mfd/simple-mfd-i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c index 7315fad618e445..52c81b18750e12 100644 --- a/drivers/mfd/simple-mfd-i2c.c +++ b/drivers/mfd/simple-mfd-i2c.c @@ -105,6 +105,7 @@ static const struct regmap_config spacemit_p1_regmap_config = { }; static const struct mfd_cell spacemit_p1_cells[] = { + { .name = "spacemit-p1-reboot", }, { .name = "spacemit-p1-regulator", }, { .name = "spacemit-p1-rtc", }, }; From 49bc8ed33083dcf2b3170706c02200fd12741bbf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 2 May 2026 21:30:51 -0400 Subject: [PATCH 011/521] UPSTREAM: spi: dt-bindings: add SpacemiT K1 SPI support Add support for the SPI controller implemented by the SpacemiT K1 SoC. Acked-by: Conor Dooley Acked-by: Troy Mitchell Reviewed-by: Rob Herring (Arm) Signed-off-by: Alex Elder Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260502-spi-spacemit-k1-v10-1-f412e1ae8a34@riscstar.com Signed-off-by: Mark Brown (cherry picked from commit b610d5333c4b93389240b62d1b6299e7e2ee2e65) Signed-off-by: Han Gao --- .../bindings/spi/spacemit,k1-spi.yaml | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml new file mode 100644 index 00000000000000..e82c7f8d0b981f --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/spacemit,k1-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 SoC Serial Peripheral Interface (SPI) + +maintainers: + - Alex Elder + +description: + The SpacemiT K1 SoC implements a SPI controller that has two 32-entry + FIFOs, for transmit and receive. Details are currently available in + section 18.2.1 of the K1 User Manual, found in the SpacemiT Keystone + K1 Documentation[1]. The controller transfers words using PIO. DMA + transfers are supported as well, if both TX and RX DMA channels are + specified, + + [1] https://developer.spacemit.com/documentation + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + const: spacemit,k1-spi + + reg: + maxItems: 1 + + clocks: + items: + - description: Core clock + - description: Bus clock + + clock-names: + items: + - const: core + - const: bus + + resets: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + items: + - description: RX DMA channel + - description: TX DMA channel + + dma-names: + items: + - const: rx + - const: tx + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - interrupts + +unevaluatedProperties: false + +examples: + - | + + #include + spi@d401c000 { + compatible = "spacemit,k1-spi"; + reg = <0xd401c000 0x30>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&syscon_apbc CLK_SSP3>, + <&syscon_apbc CLK_SSP3_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_SSP3>; + interrupts = <55>; + dmas = <&pdma 20>, <&pdma 19>; + dma-names = "rx", "tx"; + }; From 7d0a58ee813ad319d71ab696d7a1cd5d6349d58a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 2 May 2026 21:30:52 -0400 Subject: [PATCH 012/521] UPSTREAM: spi: spacemit: introduce SpacemiT K1 SPI controller driver This patch introduces the driver for the SPI controller found in the SpacemiT K1 SoC. Currently the driver supports master mode only. The SPI hardware implements RX and TX FIFOs, 32 entries each, and supports both PIO and DMA mode transfers. Signed-off-by: Alex Elder Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260502-spi-spacemit-k1-v10-2-f412e1ae8a34@riscstar.com Signed-off-by: Mark Brown (cherry picked from commit efcd8b9d111177d48c841d09beca43b15d5b9e5f) Signed-off-by: Han Gao --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-spacemit-k1.c | 789 ++++++++++++++++++++++++++++++++++ 3 files changed, 799 insertions(+) create mode 100644 drivers/spi/spi-spacemit-k1.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b563f49e219772..8782514bb89b0f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1085,6 +1085,15 @@ config SPI_SG2044_NOR also supporting 3Byte address devices and 4Byte address devices. +config SPI_SPACEMIT_K1 + tristate "K1 SPI Controller" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF + imply MMP_PDMA if ARCH_SPACEMIT + default m if ARCH_SPACEMIT + help + Enable support for the SpacemiT K1 SPI controller. + config SPI_SPRD tristate "Spreadtrum SPI controller" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9d36190a988481..9fa12498ce8c0e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -143,6 +143,7 @@ obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o obj-$(CONFIG_SPI_SG2044_NOR) += spi-sg2044-nor.o +obj-$(CONFIG_SPI_SPACEMIT_K1) += spi-spacemit-k1.o obj-$(CONFIG_SPI_SPRD) += spi-sprd.o obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o obj-$(CONFIG_SPI_STM32) += spi-stm32.o diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c new file mode 100644 index 00000000000000..99db429db0b266 --- /dev/null +++ b/drivers/spi/spi-spacemit-k1.c @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// SpacemiT K1 SPI controller driver +// +// Copyright (C) 2026, RISCstar Solutions Corporation +// Copyright (C) 2023, SpacemiT Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internals.h" + +/* This is the range of transfer rates supported by the K1 SoC */ +#define K1_SPI_MIN_SPEED_HZ 6250 +#define K1_SPI_MAX_SPEED_HZ 51200000 + +/* DMA constraints */ +#define K1_SPI_DMA_ALIGNMENT 64 +#define K1_SPI_MAX_DMA_LEN SZ_512K + +/* SSP Top Control Register */ +#define SSP_TOP_CTRL 0x00 +#define TOP_SSE BIT(0) /* Enable port */ +#define TOP_FRF_MASK GENMASK(2, 1) /* Frame format */ +#define TOP_FRF_MOTOROLA 0 /* Motorola SPI */ +#define TOP_DSS_MASK GENMASK(9, 5) /* Data size (1-32) */ +#define TOP_SPO BIT(10) /* Polarity: 0=low */ +#define TOP_SPH BIT(11) /* Half-cycle phase */ +#define TOP_LBM BIT(12) /* Loopback mode */ +#define TOP_TRAIL BIT(13) /* Trailing bytes */ +#define TOP_HOLD_FRAME_LOW BIT(14) /* Chip select */ + +/* SSP FIFO Control Register */ +#define SSP_FIFO_CTRL 0x04 +#define FIFO_TFT_MASK GENMASK(4, 0) /* TX FIFO threshold */ +#define FIFO_RFT_MASK GENMASK(9, 5) /* RX FIFO threshold */ +#define FIFO_TSRE BIT(10) /* TX service request */ +#define FIFO_RSRE BIT(11) /* RX service request */ + +/* SSP Interrupt Enable Register */ +#define SSP_INT_EN 0x08 +#define SSP_INT_EN_TINTE BIT(1) /* RX timeout */ +#define SSP_INT_EN_RIE BIT(2) /* RX FIFO */ +#define SSP_INT_EN_TIE BIT(3) /* TX FIFO */ +#define SSP_INT_EN_RIM BIT(4) /* RX FIFO overrun */ +#define SSP_INT_EN_TIM BIT(5) /* TX FIFO underrun */ +#define SSP_INT_EN_EBCEI BIT(6) /* Bit count error */ + +/* TX interrupts, RX interrupts, and error interrupts */ +#define SSP_INT_EN_TX SSP_INT_EN_TIE +#define SSP_INT_EN_RX \ + (SSP_INT_EN_TINTE | SSP_INT_EN_RIE) +#define SSP_INT_EN_ERROR \ + (SSP_INT_EN_RIM | SSP_INT_EN_TIM | SSP_INT_EN_EBCEI) + +/* SSP Time Out Register */ +#define SSP_TIMEOUT 0x0c +#define SSP_TIMEOUT_MASK GENMASK(23, 0) + +/* SSP Data Register */ +#define SSP_DATAR 0x10 + +/* SSP Status Register */ +#define SSP_STATUS 0x14 +#define SSP_STATUS_BSY BIT(0) /* SPI/I2S busy */ +#define SSP_STATUS_TNF BIT(6) /* TX FIFO not full */ +#define SSP_STATUS_TFL GENMASK(11, 7) /* TX FIFO level */ +#define SSP_STATUS_TUR BIT(12) /* TX FIFO underrun */ +#define SSP_STATUS_RNE BIT(14) /* RX FIFO not empty */ +#define SSP_STATUS_RFL GENMASK(19, 15) /* RX FIFO level */ +#define SSP_STATUS_ROR BIT(20) /* RX FIFO overrun */ +#define SSP_STATUS_BCE BIT(21) /* Bit count error */ + +/* Error status mask */ +#define SSP_STATUS_ERROR \ + (SSP_STATUS_TUR | SSP_STATUS_ROR | SSP_STATUS_BCE) + +/* The FIFO sizes and thresholds are the same for RX and TX */ +#define K1_SPI_FIFO_SIZE 32 +#define K1_SPI_THRESH (K1_SPI_FIFO_SIZE / 2) + +struct k1_spi_driver_data { + struct spi_controller *host; + void __iomem *base; + phys_addr_t base_addr; + unsigned long bus_rate; + struct clk *clk; + unsigned long rate; + int irq; + + /* Current transfer information; not valid if message is null */ + u32 bytes; /* Bytes used for bits_per_word */ + unsigned int rx_resid; /* RX bytes left in transfer */ + unsigned int tx_resid; /* TX bytes left in transfer */ + struct spi_transfer *transfer; /* Current transfer */ + + bool dma_enabled; +}; + +/* Set our registers to a known initial state */ +static void +k1_spi_register_reset(struct k1_spi_driver_data *drv_data, bool initial) +{ + u32 val = 0; + + writel(0, drv_data->base + SSP_TOP_CTRL); + + if (initial) { + /* + * The TX and RX FIFO thresholds are the same no matter + * what the speed or bits per word, so we can just set + * them once. The thresholds are one more than the values + * in the register. + */ + val = FIELD_PREP(FIFO_RFT_MASK, K1_SPI_THRESH - 1); + val |= FIELD_PREP(FIFO_TFT_MASK, K1_SPI_THRESH - 1); + } + writel(val, drv_data->base + SSP_FIFO_CTRL); + + writel(0, drv_data->base + SSP_INT_EN); + writel(0, drv_data->base + SSP_TIMEOUT); + + /* Clear any pending interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); +} + +/* + * The client can call the setup function multiple times, and each call + * can specify a different SPI mode (and transfer speed). Each transfer + * can specify its own speed though, and the core code ensures each + * transfer's speed is set to something nonzero and supported by both + * the controller and the device. We just set the speed for each transfer. + */ +static int k1_spi_setup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data = spi_controller_get_devdata(spi->controller); + + /* + * Configure the message format for this device. We only + * support Motorola SPI format in master mode. + */ + val = FIELD_PREP(TOP_FRF_MASK, TOP_FRF_MOTOROLA); + + /* Translate the mode into the value used to program the hardware. */ + if (spi->mode & SPI_CPHA) + val |= TOP_SPH; /* 1/2 cycle */ + if (spi->mode & SPI_CPOL) + val |= TOP_SPO; /* active low */ + if (spi->mode & SPI_LOOP) + val |= TOP_LBM; /* enable loopback */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + return 0; +} + +static void k1_spi_cleanup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + + drv_data = spi_controller_get_devdata(spi->controller); + k1_spi_register_reset(drv_data, false); +} + +static bool k1_spi_can_dma(struct spi_controller *host, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 burst_size; + + if (!drv_data->dma_enabled) + return false; + + if (transfer->len > SZ_2K) + return false; + + /* Don't bother with DMA if we can't do even a single burst */ + burst_size = K1_SPI_THRESH * spi_bpw_to_bytes(transfer->bits_per_word); + + return transfer->len >= burst_size; +} + +static void k1_spi_dma_callback(void *param) +{ + struct k1_spi_driver_data *drv_data = param; + u32 val; + + val = readl(drv_data->base + SSP_FIFO_CTRL); + val &= ~(FIFO_TSRE | FIFO_RSRE); + writel(val, drv_data->base + SSP_FIFO_CTRL); + + val = readl(drv_data->base + SSP_TOP_CTRL); + val &= ~TOP_TRAIL; + writel(val, drv_data->base + SSP_TOP_CTRL); + + /* Check for any error conditions */ + val = readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_ERROR) + drv_data->transfer->error |= SPI_TRANS_FAIL_IO; + + /* Disable the port */ + val = readl(drv_data->base + SSP_TOP_CTRL); + val &= ~TOP_SSE; + writel(val, drv_data->base + SSP_TOP_CTRL); + + drv_data->transfer = NULL; + + spi_finalize_current_transfer(drv_data->host); +} + +/* Prepare a descriptor for TX or RX DMA */ +static struct dma_async_tx_descriptor * +k1_spi_dma_prep(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer, bool tx) +{ + phys_addr_t addr = drv_data->base_addr + SSP_DATAR; + u32 burst_size = K1_SPI_THRESH * drv_data->bytes; + struct dma_slave_config cfg = { }; + enum dma_transfer_direction dir; + enum dma_slave_buswidth width; + struct dma_chan *chan; + struct sg_table *sgt; + + switch (drv_data->bytes) { + case 1: + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 2: + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: /* bytes == 4 */ + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + if (tx) { + chan = drv_data->host->dma_tx; + sgt = &transfer->tx_sg; + dir = DMA_MEM_TO_DEV; + + cfg.dst_addr = addr; + cfg.dst_addr_width = width; + cfg.dst_maxburst = burst_size; + } else { + chan = drv_data->host->dma_rx; + sgt = &transfer->rx_sg; + dir = DMA_DEV_TO_MEM; + + cfg.src_addr = addr; + cfg.src_addr_width = width; + cfg.src_maxburst = burst_size; + } + cfg.direction = dir; + + if (dmaengine_slave_config(chan, &cfg)) + return NULL; + + return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + +} + +static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + struct dma_async_tx_descriptor *desc; + u32 val; + + /* Prepare the TX descriptor and submit it */ + desc = k1_spi_dma_prep(drv_data, transfer, true); + if (!desc) + goto fallback; + dmaengine_submit(desc); + + /* Prepare the RX descriptor and submit it */ + desc = k1_spi_dma_prep(drv_data, transfer, false); + if (!desc) + goto fallback; + + /* When RX is complete we also know TX has completed */ + desc->callback = k1_spi_dma_callback; + desc->callback_param = drv_data; + + dmaengine_submit(desc); + + val = readl(drv_data->base + SSP_TOP_CTRL); + val |= TOP_TRAIL; /* Trailing bytes handled by DMA */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + val = readl(drv_data->base + SSP_FIFO_CTRL); + val |= FIFO_TSRE | FIFO_RSRE; + writel(val, drv_data->base + SSP_FIFO_CTRL); + + /* Start RX first so we're ready the instant we start transmitting */ + dma_async_issue_pending(host->dma_rx); + dma_async_issue_pending(host->dma_tx); + + return 1; +fallback: + transfer->error |= SPI_TRANS_FAIL_NO_START; + + return -EAGAIN; +} + +/* Flush the RX FIFO of any leftover data before processing a message */ +static int k1_spi_prepare_message(struct spi_controller *host, + struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 val = readl(drv_data->base + SSP_STATUS); + u32 count; + + /* If there's nothing in the FIFO, we're done */ + if (!(val & SSP_STATUS_RNE)) + return 0; + + /* Read and discard what's there (one more than what the field says) */ + count = FIELD_GET(SSP_STATUS_RFL, val) + 1; + do + (void)readl(drv_data->base + SSP_DATAR); + while (--count); + + return 0; +} + +/* Set logic level of chip select line (high=true means CS deasserted) */ +static void k1_spi_set_cs(struct spi_device *spi, bool high) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data = spi_controller_get_devdata(spi->controller); + + val = readl(drv_data->base + SSP_TOP_CTRL); + if (high) + val &= ~TOP_HOLD_FRAME_LOW; + else + val |= TOP_HOLD_FRAME_LOW; + writel(val, drv_data->base + SSP_TOP_CTRL); +} + +/* Set the transfer speed; the SPI core code ensures it is supported */ +static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer) +{ + struct clk *clk = drv_data->clk; + u64 nsec_per_word; + u64 bus_ticks; + u32 timeout; + u32 val; + int ret; + + ret = clk_set_rate(clk, transfer->speed_hz); + if (ret) + return ret; + + drv_data->rate = clk_get_rate(clk); + + /* No need for RX FIFO timeout if we're not receiving anything */ + if (!transfer->rx_buf) + return 0; + + /* + * Compute the RX FIFO inactivity timeout value that should be used. + * The inactivity timer restarts with each word that lands in the + * FIFO. If several "word transfer times" pass without any new data + * in the RX FIFO, we might as well read what's there. + * + * The rate at which words land in the FIFO is determined by the + * word size and the transfer rate. One bit is transferred per + * clock tick, and 8 (or 16 or 32) bits are transferred per word. + * + * So we can get word transfer time (in nanoseconds) from: + * nsec_per_tick = NSEC_PER_SEC / drv_data->rate; + * ticks_per_word = BITS_PER_BYTE * drv_data->bytes; + * We do the divide last for better accuracy. + */ + nsec_per_word = NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; + nsec_per_word = DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate); + + /* + * The timeout (which we'll set to three word transfer times) is + * expressed as a number of APB clock ticks. + * bus_ticks = 3 * nsec * (drv_data->bus_rate / NSEC_PER_SEC) + */ + bus_ticks = 3 * nsec_per_word * drv_data->bus_rate; + timeout = DIV_ROUND_UP_ULL(bus_ticks, NSEC_PER_SEC); + + /* Set the RX timeout period (required for both DMA and PIO) */ + val = FIELD_PREP(SSP_TIMEOUT_MASK, timeout); + writel(val, drv_data->base + SSP_TIMEOUT); + + return 0; +} + +static int k1_spi_transfer_one(struct spi_controller *host, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 ctrl; + u32 val; + int ret; + + /* Bits per word can change on a per-transfer basis */ + drv_data->bytes = spi_bpw_to_bytes(transfer->bits_per_word); + + /* Each transfer can also specify a different rate */ + ret = k1_spi_set_speed(drv_data, transfer); + if (ret) { + dev_err(&host->dev, + "failed to set transfer speed: %d\n", ret); + return ret; + } + + drv_data->rx_resid = transfer->len; + drv_data->tx_resid = transfer->len; + + drv_data->transfer = transfer; + + /* Clear any existing interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); + + /* Set the data (word) size, and enable the port */ + ctrl = readl(drv_data->base + SSP_TOP_CTRL); + ctrl &= ~TOP_DSS_MASK; + ctrl |= FIELD_PREP(TOP_DSS_MASK, transfer->bits_per_word - 1); + ctrl |= TOP_SSE; + writel(ctrl, drv_data->base + SSP_TOP_CTRL); + + if (spi_xfer_is_dma_mapped(host, spi, transfer)) + return k1_spi_dma_one(host, spi, transfer); + + /* An interrupt will initiate the transfer */ + val = SSP_INT_EN_TX | SSP_INT_EN_RX | SSP_INT_EN_ERROR; + writel(val, drv_data->base + SSP_INT_EN); + + return 1; /* We will call spi_finalize_current_transfer() */ +} + +static void +k1_spi_handle_err(struct spi_controller *host, struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + + if (drv_data->dma_enabled) { + dmaengine_terminate_sync(host->dma_rx); + dmaengine_terminate_sync(host->dma_tx); + } +} + +static void k1_spi_write_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer = drv_data->transfer; + u32 bytes = drv_data->bytes; + u32 val; + + if (transfer->tx_buf) { + const void *buf; + + buf = transfer->tx_buf + (transfer->len - drv_data->tx_resid); + if (bytes == 1) + val = *(u8 *)buf; + else if (bytes == 2) + val = *(u16 *)buf; + else /* bytes == 4 */ + val = *(u32 *)buf; + } else { + val = 0; /* Null writer; write 1, 2, or 4 zero bytes */ + } + /* Fill the next TX FIFO entry */ + writel(val, drv_data->base + SSP_DATAR); + + drv_data->tx_resid -= bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_TNF is set */ +static bool k1_spi_write(struct k1_spi_driver_data *drv_data, u32 val) +{ + unsigned int count; + + /* Get the number of open slots in the FIFO; zero means all */ + count = FIELD_GET(SSP_STATUS_TFL, val) ? : K1_SPI_FIFO_SIZE; + + /* + * Limit how much we try to send at a time, to reduce the + * chance the other side can overrun our RX FIFO. + */ + count = min3(count, K1_SPI_THRESH, drv_data->tx_resid / drv_data->bytes); + do + k1_spi_write_word(drv_data); + while (--count); + + return !drv_data->tx_resid; +} + +static void k1_spi_read_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer = drv_data->transfer; + u32 bytes = drv_data->bytes; + u32 val; + + /* Consume the next RX FIFO entry */ + val = readl(drv_data->base + SSP_DATAR); + if (transfer->rx_buf) { + void *buf; + + buf = transfer->rx_buf + (transfer->len - drv_data->rx_resid); + + if (bytes == 1) + *(u8 *)buf = val; + else if (bytes == 2) + *(u16 *)buf = val; + else /* bytes == 4 */ + *(u32 *)buf = val; + } /* Otherwise null reader: discard the data */ + + drv_data->rx_resid -= bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_RNE is set */ +static bool k1_spi_read(struct k1_spi_driver_data *drv_data, u32 val) +{ + do { + unsigned int count = FIELD_GET(SSP_STATUS_RFL, val) + 1; + + /* Only read what we need */ + count = min(count, drv_data->rx_resid / drv_data->bytes); + do + k1_spi_read_word(drv_data); + while (--count); + + /* If there's no more to read, we're done */ + if (!drv_data->rx_resid) + return true; + + /* Check again in case more became available to read */ + val = readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_RNE) + writel(SSP_STATUS_RNE, drv_data->base + SSP_STATUS); + else + return false; + } while (true); +} + +static irqreturn_t k1_spi_ssp_isr(int irq, void *dev_id) +{ + struct k1_spi_driver_data *drv_data = dev_id; + u32 status; + u32 top_ctrl; + + /* Get status and clear pending interrupts */ + status = readl(drv_data->base + SSP_STATUS); + writel(status, drv_data->base + SSP_STATUS); + + /* If no actionable status bits are set, this is not our interrupt */ + if (!(status & (SSP_STATUS_ERROR | SSP_STATUS_TNF | SSP_STATUS_RNE))) + return IRQ_NONE; + + /* Check for any error conditions first */ + if (status & SSP_STATUS_ERROR) { + if (drv_data->transfer) + drv_data->transfer->error |= SPI_TRANS_FAIL_IO; + goto done; + } + + /* + * For SPI, bytes are transferred in both directions equally, and + * RX always follows TX. Start by writing if there is anything to + * write, then read. Once there's no more to read, we're done. + */ + if (drv_data->tx_resid && (status & SSP_STATUS_TNF)) { + /* If we finish writing, disable TX interrupts */ + if (k1_spi_write(drv_data, status)) + writel(SSP_INT_EN_RX | SSP_INT_EN_ERROR, + drv_data->base + SSP_INT_EN); + } + + /* We're not done unless we've read all that was requested */ + if (drv_data->rx_resid) { + /* Read more if the FIFO is not empty */ + if (status & SSP_STATUS_RNE) + if (k1_spi_read(drv_data, status)) + goto done; + + return IRQ_HANDLED; + } +done: + /* Disable the port */ + top_ctrl = readl(drv_data->base + SSP_TOP_CTRL); + top_ctrl &= ~TOP_SSE; + writel(top_ctrl, drv_data->base + SSP_TOP_CTRL); + + /* Disable all interrupts */ + writel(0, drv_data->base + SSP_INT_EN); + + if (drv_data->transfer) { + drv_data->transfer = NULL; + spi_finalize_current_transfer(drv_data->host); + } + + return IRQ_HANDLED; +} + +static int +k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev) +{ + struct spi_controller *host = drv_data->host; + struct dma_chan *chan; + + chan = dma_request_chan(dev, "tx"); + if (IS_ERR(chan)) + return PTR_ERR(chan); + host->dma_tx = chan; + + chan = dma_request_chan(dev, "rx"); + if (IS_ERR(chan)) { + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; + return PTR_ERR(chan); + } + host->dma_rx = chan; + + drv_data->dma_enabled = true; + + return 0; +} + +static void k1_spi_dma_cleanup(struct device *dev, void *res) +{ + struct k1_spi_driver_data **ptr = res; + struct k1_spi_driver_data *drv_data = *ptr; + struct spi_controller *host = drv_data->host; + + if (!drv_data->dma_enabled) + return; + + drv_data->dma_enabled = false; + + dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; +} + +static int +devm_k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev) +{ + struct k1_spi_driver_data **ptr; + int ret; + + if (!IS_ENABLED(CONFIG_MMP_PDMA)) { + dev_info(dev, "DMA not available; using PIO\n"); + return 0; + } + + ptr = devres_alloc(k1_spi_dma_cleanup, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = k1_spi_dma_setup(drv_data, dev); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = drv_data; + devres_add(dev, ptr); + + return 0; +} + +static int k1_spi_probe(struct platform_device *pdev) +{ + struct k1_spi_driver_data *drv_data; + struct device *dev = &pdev->dev; + struct reset_control *reset; + struct spi_controller *host; + struct resource *iores; + struct clk *clk_bus; + int ret; + + host = devm_spi_alloc_host(dev, sizeof(*drv_data)); + if (!host) + return -ENOMEM; + drv_data = spi_controller_get_devdata(host); + drv_data->host = host; + platform_set_drvdata(pdev, drv_data); + + ret = devm_k1_spi_dma_setup(drv_data, dev); + if (ret == -EPROBE_DEFER) + return ret; + if (ret) + dev_warn(dev, "DMA setup failed (%d), falling back to PIO\n", ret); + + drv_data->base = devm_platform_get_and_ioremap_resource(pdev, 0, + &iores); + if (IS_ERR(drv_data->base)) + return dev_err_probe(dev, PTR_ERR(drv_data->base), + "error mapping memory\n"); + drv_data->base_addr = iores->start; + + clk_bus = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk_bus)) + return dev_err_probe(dev, PTR_ERR(clk_bus), + "error getting/enabling bus clock\n"); + drv_data->bus_rate = clk_get_rate(clk_bus); + + drv_data->clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(drv_data->clk)) + return dev_err_probe(dev, PTR_ERR(drv_data->clk), + "error getting/enabling core clock\n"); + + reset = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), + "error getting/deasserting reset\n"); + + k1_spi_register_reset(drv_data, true); + + drv_data->irq = platform_get_irq(pdev, 0); + if (drv_data->irq < 0) + return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n"); + + ret = devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr, + IRQF_SHARED, dev_name(dev), drv_data); + if (ret < 0) + return dev_err_probe(dev, ret, "error requesting IRQ\n"); + + /* Initialize the host structure, then register it */ + host->dev.of_node = dev_of_node(dev); + host->dev.parent = dev; + host->num_chipselect = 1; + if (drv_data->dma_enabled) + host->dma_alignment = K1_SPI_DMA_ALIGNMENT; + host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + host->min_speed_hz = K1_SPI_MIN_SPEED_HZ; + host->max_speed_hz = K1_SPI_MAX_SPEED_HZ; + host->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + host->max_dma_len = K1_SPI_MAX_DMA_LEN; + + host->setup = k1_spi_setup; + host->cleanup = k1_spi_cleanup; + host->can_dma = k1_spi_can_dma; + host->prepare_message = k1_spi_prepare_message; + host->set_cs = k1_spi_set_cs; + host->transfer_one = k1_spi_transfer_one; + host->handle_err = k1_spi_handle_err; + + ret = devm_spi_register_controller(dev, host); + if (ret) + dev_err(dev, "error registering controller\n"); + + return ret; +} + +static const struct of_device_id k1_spi_dt_ids[] = { + { .compatible = "spacemit,k1-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, k1_spi_dt_ids); + +static struct platform_driver k1_spi_driver = { + .probe = k1_spi_probe, + .driver = { + .name = "k1-spi", + .of_match_table = k1_spi_dt_ids, + }, +}; +module_platform_driver(k1_spi_driver); + +MODULE_DESCRIPTION("SpacemiT K1 SPI controller driver"); +MODULE_LICENSE("GPL"); From 9550cf8212e415f386469f564f420d1da7151517 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 5 May 2026 09:53:34 -0400 Subject: [PATCH 013/521] UPSTREAM: spi: spacemit: add u64 cast to NSEC_PER_SEC to avoid 32-bit overflow NSEC_PER_SEC expands to the long constant 1000000000L, so NSEC_PER_SEC * BITS_PER_BYTE (8 * 10^9) overflows on 32-bit-long architectures before the result reaches the u64 nsec_per_word. Promote the multiplication to u64 by casting the first operand, which is NSEC_PER_SEC. Fixes: efcd8b9d1111 ("spi: spacemit: introduce SpacemiT K1 SPI controller driver") Suggested-by: Alex Elder Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605050437.RS6mmV2b-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202605050317.Tf9j487w-lkp@intel.com/ Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260505-spi-spacemit-k1-fix-overflow-v1-1-77564c2e4e86@riscstar.com Signed-off-by: Mark Brown (cherry picked from commit 00cef7eb08c8b9af0978f667c77c6a30b71b7e22) Signed-off-by: Han Gao --- drivers/spi/spi-spacemit-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c index 99db429db0b266..215fe66d27b4db 100644 --- a/drivers/spi/spi-spacemit-k1.c +++ b/drivers/spi/spi-spacemit-k1.c @@ -390,7 +390,7 @@ static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data, * ticks_per_word = BITS_PER_BYTE * drv_data->bytes; * We do the divide last for better accuracy. */ - nsec_per_word = NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; + nsec_per_word = (u64)NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; nsec_per_word = DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate); /* From b060c7649d175f03ed4faa8078d01f31580761bf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 2 May 2026 21:30:53 -0400 Subject: [PATCH 014/521] UPSTREAM: riscv: dts: spacemit: define a SPI controller node Define a node for the fourth SoC SPI controller (number 3) on the SpacemiT K1 SoC. Enable it on the Banana Pi BPI-F3 board, which exposes this feature via its GPIO block: GPIO PIN 19: MOSI GPIO PIN 21: MISO GPIO PIN 23: SCLK GPIO PIN 24: SS (inverted) Define pincontrol configurations for the pins as used on that board. (This was tested using a GigaDevice GD25Q64E SPI NOR chip.) Reviewed-by: Yixun Lan Signed-off-by: Alex Elder Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20260502-spi-spacemit-k1-v10-3-f412e1ae8a34@riscstar.com Signed-off-by: Yixun Lan (cherry picked from commit c580774185426ea316396b1dc3f1737a3ad3800a) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-bananapi-f3.dts | 7 +++++++ arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi | 20 +++++++++++++++++++ arch/riscv/boot/dts/spacemit/k1.dtsi | 15 ++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 5790d927b93db3..9429189354d6aa 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -14,6 +14,7 @@ ethernet0 = ð0; ethernet1 = ð1; serial0 = &uart0; + spi3 = &spi3; i2c2 = &i2c2; i2c8 = &i2c8; }; @@ -335,6 +336,12 @@ status = "okay"; }; +&spi3 { + pinctrl-0 = <&ssp3_0_cfg>; + pinctrl-names = "default"; + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_2_cfg>; diff --git a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi index b13dcb10f4d660..34d88334e95e42 100644 --- a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi @@ -570,4 +570,24 @@ drive-strength = <32>; }; }; + + ssp3_0_cfg: ssp3-0-cfg { + ssp3-0-pins { + pinmux = , /* SCLK */ + , /* MOSI */ + ; /* MISO */ + + bias-disable; + drive-strength = <19>; + power-source = <3300>; + }; + + ssp3-0-frm-pins { + pinmux = ; /* FRM (frame) */ + + bias-pull-up = <0>; + drive-strength = <19>; + power-source = <3300>; + }; + }; }; diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index f0bad6855c970a..f8747190d2e127 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -983,6 +983,21 @@ status = "disabled"; }; + spi3: spi@d401c000 { + compatible = "spacemit,k1-spi"; + reg = <0x0 0xd401c000 0x0 0x30>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&syscon_apbc CLK_SSP3>, + <&syscon_apbc CLK_SSP3_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_SSP3>; + interrupts = <55>; + dmas = <&pdma 20>, <&pdma 19>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + /* sec_uart1: 0xf0612000, not available from Linux */ }; From 79f96b89c3aae37fdaae7437cbfb66651044010f Mon Sep 17 00:00:00 2001 From: Shuwei Wu Date: Mon, 27 Apr 2026 15:15:15 +0800 Subject: [PATCH 015/521] UPSTREAM: dt-bindings: thermal: Add SpacemiT K1 thermal sensor Document the SpacemiT K1 Thermal Sensor, which supports monitoring temperatures for five zones: soc, package, gpu, cluster0, and cluster1. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Shuwei Wu Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260427-k1-thermal-v5-1-df39187480ed@mailbox.org (cherry picked from commit 0309075e080306ad2e3b3148a3495b10dd252ef2) Signed-off-by: Han Gao --- .../bindings/thermal/spacemit,k1-tsensor.yaml | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml diff --git a/Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml b/Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml new file mode 100644 index 00000000000000..6dad76a7dd3613 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/spacemit,k1-tsensor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 Thermal Sensor + +description: + The SpacemiT K1 Thermal Sensor monitors the temperature of the SoC + using multiple internal sensors (e.g., soc, package, gpu, clusters). + +maintainers: + - Shuwei Wu + +$ref: thermal-sensor.yaml# + +properties: + compatible: + const: spacemit,k1-tsensor + + reg: + maxItems: 1 + + clocks: + items: + - description: Core clock for thermal sensor + - description: Bus clock for thermal sensor + + clock-names: + items: + - const: core + - const: bus + + interrupts: + maxItems: 1 + + resets: + items: + - description: Reset for the thermal sensor + + "#thermal-sensor-cells": + const: 1 + description: + The first cell indicates the sensor ID. + 0 = soc + 1 = package + 2 = gpu + 3 = cluster0 + 4 = cluster1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - resets + - "#thermal-sensor-cells" + +additionalProperties: false + +examples: + - | + #include + + thermal@d4018000 { + compatible = "spacemit,k1-tsensor"; + reg = <0xd4018000 0x100>; + clocks = <&syscon_apbc CLK_TSEN>, + <&syscon_apbc CLK_TSEN_BUS>; + clock-names = "core", "bus"; + interrupts = <61>; + resets = <&syscon_apbc RESET_TSEN>; + #thermal-sensor-cells = <1>; + }; From b442bad313c5ae7b21c5efec91562c10930e04f0 Mon Sep 17 00:00:00 2001 From: Shuwei Wu Date: Mon, 27 Apr 2026 15:15:16 +0800 Subject: [PATCH 016/521] UPSTREAM: thermal/drivers/spacemit/k1: Add thermal sensor support The thermal sensor on K1 supports monitoring five temperature zones. The driver registers these sensors with the thermal framework and supports standard operations: - Reading temperature (millidegree Celsius) - Setting high/low thresholds for interrupts Signed-off-by: Shuwei Wu Signed-off-by: Daniel Lezcano Reviewed-by: Anand Moon Reviewed-by: Troy Mitchell Reviewed-by: Yao Zi Tested-by: Anand Moon Tested-by: Vincent Legoll # OrangePi-RV2 Tested-by: Gong Shuai Link: https://patch.msgid.link/20260427-k1-thermal-v5-2-df39187480ed@mailbox.org (cherry picked from commit 296a977f2bac45759a427dc57a21edb628f5c86c) Signed-off-by: Han Gao --- drivers/thermal/Kconfig | 2 + drivers/thermal/Makefile | 1 + drivers/thermal/spacemit/Kconfig | 19 ++ drivers/thermal/spacemit/Makefile | 3 + drivers/thermal/spacemit/k1_tsensor.c | 280 ++++++++++++++++++++++++++ 5 files changed, 305 insertions(+) create mode 100644 drivers/thermal/spacemit/Kconfig create mode 100644 drivers/thermal/spacemit/Makefile create mode 100644 drivers/thermal/spacemit/k1_tsensor.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index b10080d618604d..1c4a5cd5a23ee0 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -472,6 +472,8 @@ endmenu source "drivers/thermal/renesas/Kconfig" +source "drivers/thermal/spacemit/Kconfig" + source "drivers/thermal/tegra/Kconfig" config GENERIC_ADC_THERMAL diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index bb21e7ea7fc6b7..3b249195c088ef 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -65,6 +65,7 @@ obj-y += mediatek/ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o +obj-y += spacemit/ obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o diff --git a/drivers/thermal/spacemit/Kconfig b/drivers/thermal/spacemit/Kconfig new file mode 100644 index 00000000000000..de7b5ece5af2b7 --- /dev/null +++ b/drivers/thermal/spacemit/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "SpacemiT thermal drivers" +depends on ARCH_SPACEMIT || COMPILE_TEST + +config SPACEMIT_K1_TSENSOR + tristate "SpacemiT K1 thermal sensor driver" + depends on THERMAL_OF + help + This driver provides support for the thermal sensor + integrated in the SpacemiT K1 SoC. + + The thermal sensor monitors temperatures for five thermal zones: + soc, package, gpu, cluster0, and cluster1. It supports reporting + temperature values and handling high/low threshold interrupts. + + Say Y here if you want to enable thermal monitoring on SpacemiT K1. + If compiled as a module, it will be called k1_tsensor. + +endmenu diff --git a/drivers/thermal/spacemit/Makefile b/drivers/thermal/spacemit/Makefile new file mode 100644 index 00000000000000..82b30741e4ecfc --- /dev/null +++ b/drivers/thermal/spacemit/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SPACEMIT_K1_TSENSOR) += k1_tsensor.o diff --git a/drivers/thermal/spacemit/k1_tsensor.c b/drivers/thermal/spacemit/k1_tsensor.c new file mode 100644 index 00000000000000..79222d233129dc --- /dev/null +++ b/drivers/thermal/spacemit/k1_tsensor.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thermal sensor driver for SpacemiT K1 SoC + * + * Copyright (C) 2026 Shuwei Wu + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../thermal_hwmon.h" + +#define K1_TSENSOR_PCTRL_REG 0x00 +#define K1_TSENSOR_PCTRL_ENABLE BIT(0) +#define K1_TSENSOR_PCTRL_TEMP_MODE BIT(3) +#define K1_TSENSOR_PCTRL_RAW_SEL BIT(7) + +#define K1_TSENSOR_PCTRL_CTUNE GENMASK(11, 8) +#define K1_TSENSOR_PCTRL_SW_CTRL GENMASK(21, 18) +#define K1_TSENSOR_PCTRL_HW_AUTO_MODE BIT(23) + +#define K1_TSENSOR_EN_REG 0x08 +#define K1_TSENSOR_EN_ALL GENMASK(MAX_SENSOR_NUMBER - 1, 0) + +#define K1_TSENSOR_TIME_REG 0x0C +#define K1_TSENSOR_TIME_WAIT_REF_CNT GENMASK(3, 0) +#define K1_TSENSOR_TIME_ADC_CNT_RST GENMASK(7, 4) +#define K1_TSENSOR_TIME_FILTER_PERIOD GENMASK(21, 20) +#define K1_TSENSOR_TIME_MASK GENMASK(23, 0) + +#define K1_TSENSOR_INT_CLR_REG 0x10 +#define K1_TSENSOR_INT_EN_REG 0x14 +#define K1_TSENSOR_INT_STA_REG 0x18 + +#define K1_TSENSOR_INT_EN_MASK BIT(0) +#define K1_TSENSOR_INT_MASK(x) (GENMASK(2, 1) << ((x) * 2)) + +#define K1_TSENSOR_DATA_BASE_REG 0x20 +#define K1_TSENSOR_DATA_REG(x) (K1_TSENSOR_DATA_BASE_REG + ((x) / 2) * 4) +#define K1_TSENSOR_DATA_LOW_MASK GENMASK(15, 0) +#define K1_TSENSOR_DATA_HIGH_MASK GENMASK(31, 16) + +#define K1_TSENSOR_THRSH_BASE_REG 0x40 +#define K1_TSENSOR_THRSH_REG(x) (K1_TSENSOR_THRSH_BASE_REG + ((x) * 4)) +#define K1_TSENSOR_THRSH_LOW_MASK GENMASK(15, 0) +#define K1_TSENSOR_THRSH_HIGH_MASK GENMASK(31, 16) + +#define MAX_SENSOR_NUMBER 5 + +/* Hardware offset value required for temperature calculation */ +#define TEMPERATURE_OFFSET 278 + +struct k1_tsensor_channel { + struct k1_tsensor *ts; + struct thermal_zone_device *tzd; + int id; +}; + +struct k1_tsensor { + void __iomem *base; + struct k1_tsensor_channel ch[MAX_SENSOR_NUMBER]; +}; + +static void k1_tsensor_init(struct k1_tsensor *ts) +{ + u32 val; + + /* Disable all the interrupts */ + writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG); + + /* Configure ADC sampling time and filter period */ + val = readl(ts->base + K1_TSENSOR_TIME_REG); + val &= ~K1_TSENSOR_TIME_MASK; + val |= K1_TSENSOR_TIME_FILTER_PERIOD | + K1_TSENSOR_TIME_ADC_CNT_RST | + K1_TSENSOR_TIME_WAIT_REF_CNT; + writel(val, ts->base + K1_TSENSOR_TIME_REG); + + /* + * Enable all sensors' auto mode, enable dither control, + * consecutive mode, and power up sensor. + */ + val = readl(ts->base + K1_TSENSOR_PCTRL_REG); + val &= ~K1_TSENSOR_PCTRL_SW_CTRL; + val &= ~K1_TSENSOR_PCTRL_CTUNE; + val |= K1_TSENSOR_PCTRL_RAW_SEL | + K1_TSENSOR_PCTRL_TEMP_MODE | + K1_TSENSOR_PCTRL_HW_AUTO_MODE | + K1_TSENSOR_PCTRL_ENABLE; + writel(val, ts->base + K1_TSENSOR_PCTRL_REG); + + /* Enable each sensor */ + val = readl(ts->base + K1_TSENSOR_EN_REG); + val |= K1_TSENSOR_EN_ALL; + writel(val, ts->base + K1_TSENSOR_EN_REG); +} + +static void k1_tsensor_enable_irq(struct k1_tsensor_channel *ch) +{ + struct k1_tsensor *ts = ch->ts; + u32 val; + + val = readl(ts->base + K1_TSENSOR_INT_CLR_REG); + val |= K1_TSENSOR_INT_MASK(ch->id); + writel(val, ts->base + K1_TSENSOR_INT_CLR_REG); + + val = readl(ts->base + K1_TSENSOR_INT_EN_REG); + val &= ~K1_TSENSOR_INT_MASK(ch->id); + writel(val, ts->base + K1_TSENSOR_INT_EN_REG); + + /* Enable thermal interrupt */ + val = readl(ts->base + K1_TSENSOR_INT_EN_REG); + val |= K1_TSENSOR_INT_EN_MASK; + writel(val, ts->base + K1_TSENSOR_INT_EN_REG); +} + +/* + * The conversion formula used is: + * T(m°C) = (((raw_value & mask) >> shift) - TEMPERATURE_OFFSET) * 1000 + */ +static int k1_tsensor_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz); + struct k1_tsensor *ts = ch->ts; + u32 val; + + val = readl(ts->base + K1_TSENSOR_DATA_REG(ch->id)); + if (ch->id % 2) + *temp = FIELD_GET(K1_TSENSOR_DATA_HIGH_MASK, val); + else + *temp = FIELD_GET(K1_TSENSOR_DATA_LOW_MASK, val); + + *temp -= TEMPERATURE_OFFSET; + *temp *= 1000; + + return 0; +} + +/* + * For each sensor, the hardware threshold register is 32 bits: + * - Lower 16 bits [15:0] configure the low threshold temperature. + * - Upper 16 bits [31:16] configure the high threshold temperature. + */ +static int k1_tsensor_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz); + struct k1_tsensor *ts = ch->ts; + u32 val; + + if (low >= high) + return -EINVAL; + + low = clamp_val(low / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET, + FIELD_MAX(K1_TSENSOR_THRSH_LOW_MASK)); + high = clamp_val(high / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET, + FIELD_MAX(K1_TSENSOR_THRSH_HIGH_MASK)); + + val = readl(ts->base + K1_TSENSOR_THRSH_REG(ch->id)); + + val &= ~(K1_TSENSOR_THRSH_LOW_MASK | K1_TSENSOR_THRSH_HIGH_MASK); + val |= FIELD_PREP(K1_TSENSOR_THRSH_LOW_MASK, low); + val |= FIELD_PREP(K1_TSENSOR_THRSH_HIGH_MASK, high); + + writel(val, ts->base + K1_TSENSOR_THRSH_REG(ch->id)); + + return 0; +} + +static const struct thermal_zone_device_ops k1_tsensor_ops = { + .get_temp = k1_tsensor_get_temp, + .set_trips = k1_tsensor_set_trips, +}; + +static irqreturn_t k1_tsensor_irq_thread(int irq, void *data) +{ + struct k1_tsensor *ts = (struct k1_tsensor *)data; + int mask, status, i; + + status = readl(ts->base + K1_TSENSOR_INT_STA_REG); + + for (i = 0; i < MAX_SENSOR_NUMBER; i++) { + if (status & K1_TSENSOR_INT_MASK(i)) { + mask = readl(ts->base + K1_TSENSOR_INT_CLR_REG); + mask |= K1_TSENSOR_INT_MASK(i); + writel(mask, ts->base + K1_TSENSOR_INT_CLR_REG); + thermal_zone_device_update(ts->ch[i].tzd, THERMAL_EVENT_UNSPECIFIED); + } + } + + return IRQ_HANDLED; +} + +static int k1_tsensor_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct k1_tsensor *ts; + struct reset_control *reset; + struct clk *clk; + int i, irq, ret; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ts->base)) + return dev_err_probe(dev, PTR_ERR(ts->base), "Failed to get reg\n"); + + reset = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), "Failed to get/deassert reset control\n"); + + clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get core clock\n"); + + clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get bus clock\n"); + + k1_tsensor_init(ts); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + k1_tsensor_irq_thread, + IRQF_ONESHOT, "k1_tsensor", ts); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_SENSOR_NUMBER; ++i) { + ts->ch[i].id = i; + ts->ch[i].ts = ts; + ts->ch[i].tzd = devm_thermal_of_zone_register(dev, i, ts->ch + i, &k1_tsensor_ops); + if (IS_ERR(ts->ch[i].tzd)) + return PTR_ERR(ts->ch[i].tzd); + + /* Attach sysfs hwmon attributes for userspace monitoring */ + ret = devm_thermal_add_hwmon_sysfs(dev, ts->ch[i].tzd); + if (ret) + dev_warn(dev, "Failed to add hwmon sysfs attributes\n"); + + k1_tsensor_enable_irq(ts->ch + i); + } + + platform_set_drvdata(pdev, ts); + + return 0; +} + +static const struct of_device_id k1_tsensor_dt_ids[] = { + { .compatible = "spacemit,k1-tsensor" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, k1_tsensor_dt_ids); + +static struct platform_driver k1_tsensor_driver = { + .driver = { + .name = "k1_tsensor", + .of_match_table = k1_tsensor_dt_ids, + }, + .probe = k1_tsensor_probe, +}; +module_platform_driver(k1_tsensor_driver); + +MODULE_DESCRIPTION("SpacemiT K1 Thermal Sensor Driver"); +MODULE_AUTHOR("Shuwei Wu "); +MODULE_LICENSE("GPL"); From 74ad58872a85985d56d154db5ddae3a9eed067a0 Mon Sep 17 00:00:00 2001 From: Shuwei Wu Date: Mon, 27 Apr 2026 15:15:17 +0800 Subject: [PATCH 017/521] UPSTREAM: riscv: dts: spacemit: Add thermal sensor for K1 SoC Include the Thermal Sensor node in the SpacemiT K1 dtsi with definitions for registers, clocks, and interrupts. Additionally, configure thermal zones for the soc, package, gpu, and clusters to enable temperature monitoring via the thermal framework. Tested-by: Vincent Legoll # OrangePi-RV2 Tested-by: Gong Shuai Signed-off-by: Shuwei Wu Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260427-k1-thermal-v5-3-df39187480ed@mailbox.org Signed-off-by: Yixun Lan (cherry picked from commit 247c77ff5806bcd6a5ff802718de1f0aa2bcba6c) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1.dtsi | 101 +++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index f8747190d2e127..2ed5cc24e505b2 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -339,6 +339,96 @@ }; }; + thermal-zones { + soc-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&thermal 0>; + + trips { + soc-crit { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + package-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&thermal 1>; + + trips { + package-crit { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpu-thermal { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&thermal 2>; + + trips { + gpu-alert { + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + + gpu-crit { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cluster0-thermal { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&thermal 3>; + + trips { + cluster0-alert { + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + + cluster0-crit { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cluster1-thermal { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&thermal 4>; + + trips { + cluster1-alert { + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + + cluster1-crit { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; + soc { compatible = "simple-bus"; interrupt-parent = <&plic>; @@ -494,6 +584,17 @@ #reset-cells = <1>; }; + thermal: thermal@d4018000 { + compatible = "spacemit,k1-tsensor"; + reg = <0x0 0xd4018000 0x0 0x100>; + clocks = <&syscon_apbc CLK_TSEN>, + <&syscon_apbc CLK_TSEN_BUS>; + clock-names = "core", "bus"; + interrupts = <61>; + resets = <&syscon_apbc RESET_TSEN>; + #thermal-sensor-cells = <1>; + }; + i2c6: i2c@d4018800 { compatible = "spacemit,k1-i2c"; reg = <0x0 0xd4018800 0x0 0x38>; From 868c06587b36a65453e7255793cda74946d0d90a Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:47 +0800 Subject: [PATCH 018/521] UPSTREAM: riscv: mm: Rename new_vmalloc into new_valid_map_cpus Since this mechanism is now used for the kfence pool, which comes from the linear mapping and not vmalloc, rename new_vmalloc into new_valid_map_cpus to avoid misleading readers. No functional change intended. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-3-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley (cherry picked from commit a50940adfb8f390337d6ef9afe15b66ec498aceb) Signed-off-by: Han Gao --- arch/riscv/include/asm/cacheflush.h | 6 ++--- arch/riscv/kernel/entry.S | 38 ++++++++++++++--------------- arch/riscv/mm/init.c | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index b1a2ac66579263..8c7a0ef2635adf 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -41,7 +41,7 @@ do { \ } while (0) #ifdef CONFIG_64BIT -extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; +extern u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; extern char _end[]; static inline void mark_new_valid_map(void) { @@ -52,8 +52,8 @@ static inline void mark_new_valid_map(void) * the only place this can happen is in handle_exception() where * an sfence.vma is emitted. */ - for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) - new_vmalloc[i] = -1ULL; + for (i = 0; i < ARRAY_SIZE(new_valid_map_cpus); ++i) + new_valid_map_cpus[i] = -1ULL; } #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index e189c477c0e23a..c6988983cdf7d7 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -20,44 +20,44 @@ .section .irqentry.text, "ax" -.macro new_vmalloc_check +.macro new_valid_map_cpus_check REG_S a0, TASK_TI_A0(tp) csrr a0, CSR_CAUSE /* Exclude IRQs */ - blt a0, zero, .Lnew_vmalloc_restore_context_a0 + blt a0, zero, .Lnew_valid_map_cpus_restore_context_a0 REG_S a1, TASK_TI_A1(tp) - /* Only check new_vmalloc if we are in page/protection fault */ + /* Only check new_valid_map_cpus if we are in page/protection fault */ li a1, EXC_LOAD_PAGE_FAULT - beq a0, a1, .Lnew_vmalloc_kernel_address + beq a0, a1, .Lnew_valid_map_cpus_kernel_address li a1, EXC_STORE_PAGE_FAULT - beq a0, a1, .Lnew_vmalloc_kernel_address + beq a0, a1, .Lnew_valid_map_cpus_kernel_address li a1, EXC_INST_PAGE_FAULT - bne a0, a1, .Lnew_vmalloc_restore_context_a1 + bne a0, a1, .Lnew_valid_map_cpus_restore_context_a1 -.Lnew_vmalloc_kernel_address: +.Lnew_valid_map_cpus_kernel_address: /* Is it a kernel address? */ csrr a0, CSR_TVAL - bge a0, zero, .Lnew_vmalloc_restore_context_a1 + bge a0, zero, .Lnew_valid_map_cpus_restore_context_a1 /* Check if a new vmalloc mapping appeared that could explain the trap */ REG_S a2, TASK_TI_A2(tp) /* * Computes: - * a0 = &new_vmalloc[BIT_WORD(cpu)] + * a0 = &new_valid_map_cpus[BIT_WORD(cpu)] * a1 = BIT_MASK(cpu) */ lw a2, TASK_TI_CPU(tp) /* - * Compute the new_vmalloc element position: + * Compute the new_valid_map_cpus element position: * (cpu / 64) * 8 = (cpu >> 6) << 3 */ srli a1, a2, 6 slli a1, a1, 3 - la a0, new_vmalloc + la a0, new_valid_map_cpus add a0, a0, a1 /* - * Compute the bit position in the new_vmalloc element: + * Compute the bit position in the new_valid_map_cpus element: * bit_pos = cpu % 64 = cpu - (cpu / 64) * 64 = cpu - (cpu >> 6) << 6 * = cpu - ((cpu >> 6) << 3) << 3 */ @@ -67,12 +67,12 @@ li a2, 1 sll a1, a2, a1 - /* Check the value of new_vmalloc for this cpu */ + /* Check the value of new_valid_map_cpus for this cpu */ REG_L a2, 0(a0) and a2, a2, a1 - beq a2, zero, .Lnew_vmalloc_restore_context + beq a2, zero, .Lnew_valid_map_cpus_restore_context - /* Atomically reset the current cpu bit in new_vmalloc */ + /* Atomically reset the current cpu bit in new_valid_map_cpus */ amoxor.d a0, a1, (a0) /* @@ -87,11 +87,11 @@ csrw CSR_SCRATCH, x0 sret -.Lnew_vmalloc_restore_context: +.Lnew_valid_map_cpus_restore_context: REG_L a2, TASK_TI_A2(tp) -.Lnew_vmalloc_restore_context_a1: +.Lnew_valid_map_cpus_restore_context_a1: REG_L a1, TASK_TI_A1(tp) -.Lnew_vmalloc_restore_context_a0: +.Lnew_valid_map_cpus_restore_context_a0: REG_L a0, TASK_TI_A0(tp) .endm @@ -149,7 +149,7 @@ SYM_CODE_START(handle_exception) * could "miss" the new mapping and traps: in that case, we only need * to retry the access, no sfence.vma is required. */ - new_vmalloc_check + new_valid_map_cpus_check #endif REG_S sp, TASK_TI_KERNEL_SP(tp) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index fa8d2f6f554b57..bede6845f76721 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,7 @@ #include "../kernel/head.h" -u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; +u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); From 09f005e396474faa43e28db50e35e2d66564dc1b Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:48 +0800 Subject: [PATCH 019/521] UPSTREAM: riscv: mm: Use the bitmap API for new_valid_map_cpus The bitmap was defined with incorrect size. Fix it by using the proper bitmap API in C code. The corresponding assembly code is still okay and remains unchanged. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-4-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley (cherry picked from commit 26c171fc48539ac88d3697792b1fde9334af836c) Signed-off-by: Han Gao --- arch/riscv/include/asm/cacheflush.h | 8 +++----- arch/riscv/mm/init.c | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 8c7a0ef2635adf..8cfe59483a8f28 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -41,19 +41,17 @@ do { \ } while (0) #ifdef CONFIG_64BIT -extern u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; +/* This is accessed in assembly code. cpumask_var_t would be too complex. */ +extern DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); extern char _end[]; static inline void mark_new_valid_map(void) { - int i; - /* * We don't care if concurrently a cpu resets this value since * the only place this can happen is in handle_exception() where * an sfence.vma is emitted. */ - for (i = 0; i < ARRAY_SIZE(new_valid_map_cpus); ++i) - new_valid_map_cpus[i] = -1ULL; + bitmap_fill(new_valid_map_cpus, NR_CPUS); } #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index bede6845f76721..ea8766c4f3a23c 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,7 @@ #include "../kernel/head.h" -u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; +DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); From ee28c1802d8781d11a0f7c5d246e0665ab1b7724 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 5 Mar 2026 01:00:51 +0000 Subject: [PATCH 020/521] UPSTREAM: dt-bindings: phy: spacemit: k3: add USB2 PHY support Introduce a compatible string for the USB2 PHY in SpacemiT K3 SoC. The IP of USB2 PHY mostly shares the same functionalities with K1 SoC, while has some register layout changes. Acked-by: Krzysztof Kozlowski Signed-off-by: Yixun Lan Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=c05cf9d274daf72dc7e433480cf2e0e888f6bd89 [1] Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=00b4fe5be06aecd6426930de86b7cffc2330f4b8 [2] Link: https://patch.msgid.link/20260305-11-k3-usb2-phy-v4-1-15554fb933bc@kernel.org Signed-off-by: Vinod Koul (cherry picked from commit ad8fdebd40fd25e86331886f4fc6951531691319) Signed-off-by: Han Gao --- .../devicetree/bindings/phy/spacemit,usb2-phy.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml index 43eaca90d88c02..18025e5f60d691 100644 --- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml @@ -4,14 +4,16 @@ $id: http://devicetree.org/schemas/phy/spacemit,usb2-phy.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: SpacemiT K1 SoC USB 2.0 PHY +title: SpacemiT K1/K3 SoC USB 2.0 PHY maintainers: - Ze Huang properties: compatible: - const: spacemit,k1-usb2-phy + enum: + - spacemit,k1-usb2-phy + - spacemit,k3-usb2-phy reg: maxItems: 1 From 5c51287f3b470423d8f0ee2f8900369e2cd72912 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 5 Mar 2026 01:00:52 +0000 Subject: [PATCH 021/521] UPSTREAM: phy: k1-usb: k3: add USB2 PHY support Add USB2 PHY support for SpacemiT K3 SoC. Register layout of handling USB disconnect operation has been changed, So introducing a platform data to distinguish the different SoCs. Reviewed-by: Yao Zi Signed-off-by: Yixun Lan Reviewed-by: Neil Armstrong Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=c05cf9d274daf72dc7e433480cf2e0e888f6bd89 [1] Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=00b4fe5be06aecd6426930de86b7cffc2330f4b8 [2] Link: https://patch.msgid.link/20260305-11-k3-usb2-phy-v4-2-15554fb933bc@kernel.org Signed-off-by: Vinod Koul (cherry picked from commit 056ee8b37bc91e3230afa11ec1018fa898b983b8) Signed-off-by: Han Gao --- drivers/phy/spacemit/phy-k1-usb2.c | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c index e8c1e26428a910..8df12eeb20b184 100644 --- a/drivers/phy/spacemit/phy-k1-usb2.c +++ b/drivers/phy/spacemit/phy-k1-usb2.c @@ -51,6 +51,9 @@ #define PHY_K1_HS_HOST_DISC 0x40 #define PHY_K1_HS_HOST_DISC_CLR BIT(0) +#define PHY_K3_HS_HOST_DISC 0x20 +#define PHY_K3_HS_HOST_DISC_CLR BIT(8) + #define PHY_PLL_DIV_CFG 0x98 #define PHY_FDIV_FRACT_8_15 GENMASK(7, 0) #define PHY_FDIV_FRACT_16_19 GENMASK(11, 8) @@ -144,7 +147,7 @@ static int spacemit_usb2phy_exit(struct phy *phy) return 0; } -static int spacemit_usb2phy_disconnect(struct phy *phy, int port) +static int spacemit_k1_usb2phy_disconnect(struct phy *phy, int port) { struct spacemit_usb2phy *sphy = phy_get_drvdata(phy); @@ -154,10 +157,27 @@ static int spacemit_usb2phy_disconnect(struct phy *phy, int port) return 0; } -static const struct phy_ops spacemit_usb2phy_ops = { +static int spacemit_k3_usb2phy_disconnect(struct phy *phy, int port) +{ + struct spacemit_usb2phy *sphy = phy_get_drvdata(phy); + + regmap_update_bits(sphy->regmap_base, PHY_K3_HS_HOST_DISC, + PHY_K3_HS_HOST_DISC_CLR, PHY_K3_HS_HOST_DISC_CLR); + + return 0; +} + +static const struct phy_ops spacemit_k1_usb2phy_ops = { .init = spacemit_usb2phy_init, .exit = spacemit_usb2phy_exit, - .disconnect = spacemit_usb2phy_disconnect, + .disconnect = spacemit_k1_usb2phy_disconnect, + .owner = THIS_MODULE, +}; + +static const struct phy_ops spacemit_k3_usb2phy_ops = { + .init = spacemit_usb2phy_init, + .exit = spacemit_usb2phy_exit, + .disconnect = spacemit_k3_usb2phy_disconnect, .owner = THIS_MODULE, }; @@ -166,12 +186,15 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct spacemit_usb2phy *sphy; + const struct phy_ops *ops; void __iomem *base; sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); if (!sphy) return -ENOMEM; + ops = device_get_match_data(dev); + sphy->clk = devm_clk_get_prepared(&pdev->dev, NULL); if (IS_ERR(sphy->clk)) return dev_err_probe(dev, PTR_ERR(sphy->clk), "Failed to get clock\n"); @@ -184,7 +207,7 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev) if (IS_ERR(sphy->regmap_base)) return dev_err_probe(dev, PTR_ERR(sphy->regmap_base), "Failed to init regmap\n"); - sphy->phy = devm_phy_create(dev, NULL, &spacemit_usb2phy_ops); + sphy->phy = devm_phy_create(dev, NULL, ops); if (IS_ERR(sphy->phy)) return dev_err_probe(dev, PTR_ERR(sphy->phy), "Failed to create phy\n"); @@ -195,7 +218,8 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev) } static const struct of_device_id spacemit_usb2phy_dt_match[] = { - { .compatible = "spacemit,k1-usb2-phy", }, + { .compatible = "spacemit,k1-usb2-phy", .data = &spacemit_k1_usb2phy_ops }, + { .compatible = "spacemit,k3-usb2-phy", .data = &spacemit_k3_usb2phy_ops }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spacemit_usb2phy_dt_match); From 6cf2048fe6520a2be7e60dda87121eefd1335a56 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 01:56:09 +0800 Subject: [PATCH 022/521] UPSTREAM: drm: verisilicon: add max cursor size to HWDB Different display controller variants support different maximum cursor size. All known DC8200 variants support both 32x32 and 64x64, but some DC8000 variants support either only 32x32 or up to 256x256. The minimum size is fixed at 32 and only PoT square sizes are supported. Add the max cursor size field to HWDB and fill all entries with 64. Signed-off-by: Icenowy Zheng Reviewed-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260506175610.2542888-2-zhengxingda@iscas.ac.cn (cherry picked from commit 9637867d78a7bc5e76b17fe6cb0be5e054fce973) Signed-off-by: Han Gao --- drivers/gpu/drm/verisilicon/vs_hwdb.c | 4 ++++ drivers/gpu/drm/verisilicon/vs_hwdb.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c b/drivers/gpu/drm/verisilicon/vs_hwdb.c index 09336af0900ae3..2a0f7c59afa3a5 100644 --- a/drivers/gpu/drm/verisilicon/vs_hwdb.c +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c @@ -95,6 +95,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = ~0U, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_no_yuv444, }, { @@ -103,6 +104,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = 0x30B, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_no_yuv444, }, { @@ -111,6 +113,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = 0x310, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_with_yuv444, }, { @@ -119,6 +122,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = 0x311, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_no_yuv444, }, }; diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h b/drivers/gpu/drm/verisilicon/vs_hwdb.h index 92192e4fa08625..2065ecb7304379 100644 --- a/drivers/gpu/drm/verisilicon/vs_hwdb.h +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h @@ -20,6 +20,11 @@ struct vs_chip_identity { u32 customer_id; u32 display_count; + /* + * The hardware only supports square cursor planes, so this field + * is both the maximum width and height in pixels. + */ + int32_t max_cursor_size; const struct vs_formats *formats; }; From b0972da596679122c9e4c03a8f447215e21ff09d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 01:56:10 +0800 Subject: [PATCH 023/521] UPSTREAM: drm: verisilicon: add support for cursor planes Verisilicon display controllers support hardware cursors per output port. Add support for them as cursor planes. Signed-off-by: Icenowy Zheng Reviewed-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260506175610.2542888-3-zhengxingda@iscas.ac.cn (cherry picked from commit 8c4ae218912554ef427440293cdb97319d17ca5d) Signed-off-by: Han Gao --- drivers/gpu/drm/verisilicon/Makefile | 3 +- drivers/gpu/drm/verisilicon/vs_crtc.c | 11 +- drivers/gpu/drm/verisilicon/vs_cursor_plane.c | 272 ++++++++++++++++++ .../drm/verisilicon/vs_cursor_plane_regs.h | 44 +++ drivers/gpu/drm/verisilicon/vs_plane.h | 1 + 5 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/verisilicon/vs_cursor_plane.c create mode 100644 drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile index fd8d805fbcde13..426f4bcaa834d5 100644 --- a/drivers/gpu/drm/verisilicon/Makefile +++ b/drivers/gpu/drm/verisilicon/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o vs_hwdb.o vs_plane.o vs_primary_plane.o +verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o vs_hwdb.o \ + vs_plane.o vs_primary_plane.o vs_cursor_plane.o obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c index f4940171300067..5c9714a3e69a7e 100644 --- a/drivers/gpu/drm/verisilicon/vs_crtc.c +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c @@ -159,7 +159,7 @@ struct vs_crtc *vs_crtc_init(struct drm_device *drm_dev, struct vs_dc *dc, unsigned int output) { struct vs_crtc *vcrtc; - struct drm_plane *primary; + struct drm_plane *primary, *cursor; int ret; vcrtc = drmm_kzalloc(drm_dev, sizeof(*vcrtc), GFP_KERNEL); @@ -175,9 +175,16 @@ struct vs_crtc *vs_crtc_init(struct drm_device *drm_dev, struct vs_dc *dc, return ERR_PTR(PTR_ERR(primary)); } + /* Create our cursor plane */ + cursor = vs_cursor_plane_init(drm_dev, dc); + if (IS_ERR(cursor)) { + drm_err(drm_dev, "Couldn't create the cursor plane\n"); + return ERR_CAST(cursor); + } + ret = drmm_crtc_init_with_planes(drm_dev, &vcrtc->base, primary, - NULL, + cursor, &vs_crtc_funcs, NULL); if (ret) { diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c new file mode 100644 index 00000000000000..acb9854fa67ed7 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS) + * + * Authors: + * Icenowy Zheng + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vs_crtc.h" +#include "vs_plane.h" +#include "vs_dc.h" +#include "vs_hwdb.h" +#include "vs_cursor_plane_regs.h" + +#define VSDC_MIN_CURSOR_SIZE 32 +#define VSDC_MAX_CURSOR_SIZE 256 + +#define VSDC_CURSOR_LOCATION_MAX_POSITIVE BIT_MASK(15) +#define VSDC_CURSOR_LOCATION_MAX_NEGATIVE BIT_MASK(5) + +static bool vs_cursor_plane_check_coord(int32_t coord) +{ + if (coord >= 0) + return coord <= VSDC_CURSOR_LOCATION_MAX_POSITIVE; + else + return (-coord) <= VSDC_CURSOR_LOCATION_MAX_NEGATIVE; +} + +static int vs_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_framebuffer *fb = new_plane_state->fb; + struct drm_crtc_state *crtc_state = NULL; + struct vs_crtc *vcrtc; + struct vs_dc *dc; + int ret; + + if (crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, + crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); + if (ret) + return ret; + + if (!new_plane_state->visible) + return 0; /* Skip validity check */ + + vcrtc = drm_crtc_to_vs_crtc(crtc); + dc = vcrtc->dc; + + /* Only certain PoT square sizes is supported. */ + if (!is_power_of_2(new_plane_state->crtc_w) || + new_plane_state->crtc_w < VSDC_MIN_CURSOR_SIZE || + new_plane_state->crtc_w > dc->identity.max_cursor_size) + return -EINVAL; + + if (new_plane_state->crtc_w != new_plane_state->crtc_h) + return -EINVAL; + + /* Check if the cursor is inside the register fields' range */ + if (!vs_cursor_plane_check_coord(new_plane_state->crtc_x) || + !vs_cursor_plane_check_coord(new_plane_state->crtc_y)) + return -EINVAL; + + /* Extra line padding isn't supported */ + if (fb->pitches[0] != + drm_format_info_min_pitch(fb->format, 0, new_plane_state->crtc_w)) + return -EINVAL; + + return 0; +} + +static void vs_cursor_plane_commit(struct vs_dc *dc, unsigned int output) +{ + regmap_set_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_COMMIT | + VSDC_CURSOR_CONFIG_IMG_UPDATE); +} + +static void vs_cursor_plane_atomic_enable(struct drm_plane *plane, + struct drm_atomic_state *atomic_state) +{ + struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, + plane); + struct drm_crtc *crtc = state->crtc; + struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); + unsigned int output = vcrtc->id; + struct vs_dc *dc = vcrtc->dc; + + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_FMT_MASK, + VSDC_CURSOR_CONFIG_FMT_ARGB8888); + + vs_cursor_plane_commit(dc, output); +} + +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *atomic_state) +{ + struct drm_plane_state *state = drm_atomic_get_old_plane_state(atomic_state, + plane); + struct drm_crtc *crtc = state->crtc; + struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); + unsigned int output = vcrtc->id; + struct vs_dc *dc = vcrtc->dc; + + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_FMT_MASK, + VSDC_CURSOR_CONFIG_FMT_OFF); + + vs_cursor_plane_commit(dc, output); +} + +static void vs_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *atomic_state) +{ + struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, + plane); + struct drm_framebuffer *fb = state->fb; + struct drm_crtc *crtc = state->crtc; + struct vs_dc *dc; + struct vs_crtc *vcrtc; + unsigned int output; + dma_addr_t dma_addr; + + if (!state->visible) { + vs_cursor_plane_atomic_disable(plane, atomic_state); + return; + } + + vcrtc = drm_crtc_to_vs_crtc(crtc); + output = vcrtc->id; + dc = vcrtc->dc; + + /* Other sizes should be rejected by atomic_check */ + switch (state->crtc_w) { + case 32: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_32); + break; + case 64: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_64); + break; + case 128: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_128); + break; + case 256: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_256); + break; + } + + dma_addr = vs_fb_get_dma_addr(fb, &state->src); + + regmap_write(dc->regs, VSDC_CURSOR_ADDRESS(output), + lower_32_bits(dma_addr)); + + /* + * The X_OFF and Y_OFF fields define which point does the LOCATION + * register represent in the cursor image, and LOCATION register + * values are unsigned. To for positive left-top coordinates the + * offset is set to 0 and the location is set to the coordinate, for + * negative coordinates the location is set to 0 and the offset + * is set to the opposite number of the coordinate to offset the + * cursor image partly off-screen. + */ + if (state->crtc_x >= 0) { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_X_OFF_MASK, 0); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_X_MASK, + VSDC_CURSOR_LOCATION_X(state->crtc_x)); + } else { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_X_OFF_MASK, + -state->crtc_x); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_X_MASK, 0); + } + + if (state->crtc_y >= 0) { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_Y_OFF_MASK, 0); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_Y_MASK, + VSDC_CURSOR_LOCATION_Y(state->crtc_y)); + } else { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_Y_OFF_MASK, + -state->crtc_y); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_Y_MASK, 0); + } + + vs_cursor_plane_commit(dc, output); +} + +static const struct drm_plane_helper_funcs vs_cursor_plane_helper_funcs = { + .atomic_check = vs_cursor_plane_atomic_check, + .atomic_update = vs_cursor_plane_atomic_update, + .atomic_enable = vs_cursor_plane_atomic_enable, + .atomic_disable = vs_cursor_plane_atomic_disable, +}; + +static const struct drm_plane_funcs vs_cursor_plane_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, +}; + +static const u32 vs_cursor_plane_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static const u64 vs_cursor_plane_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, /* sentinel */ +}; + +struct drm_plane *vs_cursor_plane_init(struct drm_device *drm_dev, + struct vs_dc *dc) +{ + int32_t max_cursor_size = dc->identity.max_cursor_size; + struct drm_plane *plane; + + if (drm_WARN_ON_ONCE(drm_dev, max_cursor_size < VSDC_MIN_CURSOR_SIZE || + max_cursor_size > VSDC_MAX_CURSOR_SIZE)) + return ERR_PTR(-EINVAL); + + plane = drmm_universal_plane_alloc(drm_dev, struct drm_plane, dev, 0, + &vs_cursor_plane_funcs, + vs_cursor_plane_formats, + ARRAY_SIZE(vs_cursor_plane_formats), + vs_cursor_plane_modifiers, + DRM_PLANE_TYPE_CURSOR, + NULL); + + if (IS_ERR(plane)) + return plane; + + drm_plane_helper_add(plane, &vs_cursor_plane_helper_funcs); + + return plane; +} diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h b/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h new file mode 100644 index 00000000000000..99693f2c95b94b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025 Icenowy Zheng + * + * Based on vs_dc_hw.h, which is: + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef _VS_CURSOR_PLANE_REGS_H_ +#define _VS_CURSOR_PLANE_REGS_H_ + +#include + +#define VSDC_CURSOR_CONFIG(n) (0x1468 + 0x1080 * (n)) +#define VSDC_CURSOR_CONFIG_FMT_MASK GENMASK(1, 0) +#define VSDC_CURSOR_CONFIG_FMT_ARGB8888 (0x2 << 0) +#define VSDC_CURSOR_CONFIG_FMT_OFF (0x0 << 0) +#define VSDC_CURSOR_CONFIG_IMG_UPDATE BIT(2) +#define VSDC_CURSOR_CONFIG_COMMIT BIT(3) +#define VSDC_CURSOR_CONFIG_SIZE_MASK GENMASK(7, 5) +#define VSDC_CURSOR_CONFIG_SIZE_32 (0x0 << 5) +#define VSDC_CURSOR_CONFIG_SIZE_64 (0x1 << 5) +#define VSDC_CURSOR_CONFIG_SIZE_128 (0x2 << 5) +#define VSDC_CURSOR_CONFIG_SIZE_256 (0x3 << 5) +#define VSDC_CURSOR_CONFIG_Y_OFF_MASK GENMASK(12, 8) +#define VSDC_CURSOR_CONFIG_Y_OFF(v) ((v) << 8) +#define VSDC_CURSOR_CONFIG_X_OFF_MASK GENMASK(20, 16) +#define VSDC_CURSOR_CONFIG_X_OFF(v) ((v) << 16) + +#define VSDC_CURSOR_ADDRESS(n) (0x146C + 0x1080 * (n)) + +#define VSDC_CURSOR_LOCATION(n) (0x1470 + 0x1080 * (n)) +#define VSDC_CURSOR_LOCATION_X_MASK GENMASK(14, 0) +#define VSDC_CURSOR_LOCATION_X(v) ((v) << 0) +#define VSDC_CURSOR_LOCATION_Y_MASK GENMASK(30, 16) +#define VSDC_CURSOR_LOCATION_Y(v) ((v) << 16) + +#define VSDC_CURSOR_BACKGROUND(n) (0x1474 + 0x1080 * (n)) +#define VSDC_CURSOR_BACKGRUOND_DEFAULT 0x00FFFFFF + +#define VSDC_CURSOR_FOREGROUND(n) (0x1478 + 0x1080 * (n)) +#define VSDC_CURSOR_FOREGRUOND_DEFAULT 0x00AAAAAA + +#endif /* _VS_CURSOR_PLANE_REGS_H_ */ diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h index 41875ea3d66a5a..60b5b3a1bc22aa 100644 --- a/drivers/gpu/drm/verisilicon/vs_plane.h +++ b/drivers/gpu/drm/verisilicon/vs_plane.h @@ -68,5 +68,6 @@ dma_addr_t vs_fb_get_dma_addr(struct drm_framebuffer *fb, const struct drm_rect *src_rect); struct drm_plane *vs_primary_plane_init(struct drm_device *dev, struct vs_dc *dc); +struct drm_plane *vs_cursor_plane_init(struct drm_device *dev, struct vs_dc *dc); #endif /* _VS_PLANE_H_ */ From 989ebbe5aca43804b584207bcea70fad8ac3fe14 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Tue, 7 Apr 2026 23:28:14 +0800 Subject: [PATCH 024/521] UPSTREAM: riscv: dts: spacemit: Enable i2c8 adapter for OrangePi RV2 The adapter is used to access the SpacemiT P1 PMIC present in this board. Tested-by: Vincent Legoll # OrangePi-RV2 Signed-off-by: Han Gao Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/d66f897b179271f508ac5352e52b1223f3bca5fb.1775575436.git.gaohan@iscas.ac.cn Signed-off-by: Yixun Lan (cherry picked from commit ece83d42785e12ab7690c0c13b416baf46a74063) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index 7b7331cb3c726f..57ec1cc32b03d7 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -87,6 +87,12 @@ status = "okay"; }; +&i2c8 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c8_cfg>; + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_2_cfg>; From da56450351dfd4208dc10e74c9d46cedaa372cb7 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Tue, 7 Apr 2026 23:28:15 +0800 Subject: [PATCH 025/521] UPSTREAM: riscv: dts: spacemit: Define the P1 PMIC regulators for OrangePi RV2 Define the DC power input and the 4v power as fixed regulator supplies. Define the SpacemiT P1 PMIC voltage regulators and their constraints. Co-developed-by: Chukun Pan Signed-off-by: Chukun Pan Tested-by: Vincent Legoll # OrangePi-RV2 Signed-off-by: Han Gao Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/193370a69e1cc3e4d8c4eb57bfea208e29ac75d8.1775575436.git.gaohan@iscas.ac.cn Signed-off-by: Yixun Lan (cherry picked from commit c02c047b925c0ed101ec6834e1c07a53a45f98d4) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-orangepi-rv2.dts | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index 57ec1cc32b03d7..f7a1dadaa95f7f 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -23,6 +23,25 @@ stdout-path = "serial0"; }; + vcc_5v0: regulator-vcc-5v0 { + compatible = "regulator-fixed"; + regulator-name = "vcc_5v0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc4v0: regulator-vcc4v0 { + compatible = "regulator-fixed"; + regulator-name = "vcc4v0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <4000000>; + vin-supply = <&vcc_5v0>; + }; + leds { compatible = "gpio-leds"; @@ -91,6 +110,118 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c8_cfg>; status = "okay"; + + pmic@41 { + compatible = "spacemit,p1"; + reg = <0x41>; + interrupts = <64>; + vin1-supply = <&vcc4v0>; + vin2-supply = <&vcc4v0>; + vin3-supply = <&vcc4v0>; + vin4-supply = <&vcc4v0>; + vin5-supply = <&vcc4v0>; + vin6-supply = <&vcc4v0>; + aldoin-supply = <&vcc4v0>; + dldoin1-supply = <&buck5>; + dldoin2-supply = <&buck5>; + + regulators { + buck1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck3_1v8: buck3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck4_3v3: buck4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck5: buck5 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + aldo1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + }; + + aldo2 { + /* not connected */ + }; + + aldo3 { + /* not connected */ + }; + + aldo4 { + /* not connected */ + }; + + dldo1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + }; + + dldo2 { + /* not connected */ + }; + + dldo3 { + /* not connected */ + }; + + dldo4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-always-on; + }; + + dldo5 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-always-on; + }; + + dldo7 { + /* not connected */ + }; + }; + }; }; &uart0 { From 5856fa677aa6ce1bb5a3b39e19daa82b13f947e9 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Tue, 7 Apr 2026 23:28:16 +0800 Subject: [PATCH 026/521] UPSTREAM: riscv: dts: spacemit: Enable USB3.0/PCIe on OrangePi RV2 Enable the DWC3 USB 3.0 controller and its associated usbphy2 on the OrangePi RV2 board. The board utilizes a Genesys Logic GL3523 USB3.0 hub. Define a 3.3v fixed voltage regulator for PCIe and enable PCIe and PHY-related Device Tree nodes for the OrangePi RV2. Co-developed-by: Chukun Pan Signed-off-by: Chukun Pan Tested-by: Vincent Legoll # OrangePi-RV2 Signed-off-by: Han Gao Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/8e397efb06efd9b02788df07f435ce153de05cd5.1775575436.git.gaohan@iscas.ac.cn Signed-off-by: Yixun Lan (cherry picked from commit e3e433dd9aa04448f8f98d59359cab1340d994a3) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-orangepi-rv2.dts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index f7a1dadaa95f7f..3a829e3c9cbcd4 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -23,6 +23,16 @@ stdout-path = "serial0"; }; + pcie_vcc3v3: regulator-pcie-vcc3v3 { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio K1_GPIO(116) GPIO_ACTIVE_HIGH>; + regulator-name = "pcie_vcc3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_5v0>; + }; + vcc_5v0: regulator-vcc-5v0 { compatible = "regulator-fixed"; regulator-name = "vcc_5v0"; @@ -42,6 +52,16 @@ vin-supply = <&vcc_5v0>; }; + vcc5v0_usb30: regulator-vcc5v0-usb30 { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio K1_GPIO(123) GPIO_ACTIVE_HIGH>; + regulator-name = "vcc5v0_usb30"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_5v0>; + }; + leds { compatible = "gpio-leds"; @@ -54,6 +74,10 @@ }; }; +&combo_phy { + status = "okay"; +}; + ð0 { phy-handle = <&rgmii0>; phy-mode = "rgmii-id"; @@ -224,8 +248,64 @@ }; }; +&pcie1_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_3_cfg>; + status = "okay"; +}; + +&pcie1_port { + phys = <&pcie1_phy>; + vpcie3v3-supply = <&pcie_vcc3v3>; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_4_cfg>; + status = "okay"; +}; + +&pcie2_port { + phys = <&pcie2_phy>; + vpcie3v3-supply = <&pcie_vcc3v3>; +}; + +&pcie2 { + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_2_cfg>; status = "okay"; }; + +&usbphy2 { + status = "okay"; +}; + +&usb_dwc3 { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + vbus-supply = <&vcc5v0_usb30>; + status = "okay"; + + hub_2_0: hub@1 { + compatible = "usb5e3,610"; + reg = <0x1>; + peer-hub = <&hub_3_0>; + vdd-supply = <&vcc_5v0>; + }; + + hub_3_0: hub@2 { + compatible = "usb5e3,620"; + reg = <0x2>; + peer-hub = <&hub_2_0>; + vdd-supply = <&vcc_5v0>; + }; +}; From 15a5c426d24a882c0f086945048995b5e03c4101 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:41 +0800 Subject: [PATCH 027/521] UPSTREAM: dt-bindings: dmaengine: Add SpacemiT K3 DMA compatible string Add the "spacemit,k3-pdma" compatible string for the SpacemiT K3 SoC. While the K3 PDMA IP reuses most of the design found on the earlier K1 SoC, a new compatible string is required because the DRCMR (DMA Request/Command Register) base address for extended DMA request numbers (>= 64) differs from the K1 implementation. Signed-off-by: Guodong Xu Acked-by: Conor Dooley Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-1-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul (cherry picked from commit 55620b11186c81757b05fb8e2df9ddc7127d6fd2) Signed-off-by: Han Gao --- Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml index ec06235baf5ca3..62ce6d81526b44 100644 --- a/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml +++ b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: spacemit,k1-pdma + enum: + - spacemit,k1-pdma + - spacemit,k3-pdma reg: maxItems: 1 From 3a30150bbb080b84b8dd6ac101c9c258f2fe0049 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:42 +0800 Subject: [PATCH 028/521] UPSTREAM: dmaengine: mmp_pdma: refactor DRCMR access with helper function Refactor the DRCMR macro into a helper function mmp_pdma_get_drcmr() to support variable extended DRCMR base addresses across different PDMA implementations, such as SpacemiT K3. Signed-off-by: Guodong Xu Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-2-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul (cherry picked from commit f46b47623e70dea8b03794a5420ffba060425e85) Signed-off-by: Han Gao --- drivers/dma/mmp_pdma.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index d12e729ee12c56..6112369006eed3 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -51,7 +51,9 @@ #define DCSR_CMPST BIT(10) /* The Descriptor Compare Status */ #define DCSR_EORINTR BIT(9) /* The end of Receive */ -#define DRCMR(n) ((((n) < 64) ? 0x0100 : 0x1100) + (((n) & 0x3f) << 2)) +#define DRCMR_BASE 0x0100 +#define DRCMR_EXT_BASE_DEFAULT 0x1100 +#define DRCMR_REQ_LIMIT 64 #define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */ #define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */ @@ -154,6 +156,7 @@ struct mmp_pdma_phy { * @run_bits: Control bits in DCSR register for channel start/stop * @dma_width: DMA addressing width in bits (32 or 64). Determines the * DMA mask capability of the controller hardware. + * @drcmr_ext_base: Base DRCMR address for extended requests */ struct mmp_pdma_ops { /* Hardware Register Operations */ @@ -174,6 +177,7 @@ struct mmp_pdma_ops { /* Controller Configuration */ u32 run_bits; u32 dma_width; + u32 drcmr_ext_base; }; struct mmp_pdma_device { @@ -195,6 +199,13 @@ struct mmp_pdma_device { #define to_mmp_pdma_dev(dmadev) \ container_of(dmadev, struct mmp_pdma_device, device) +static u32 mmp_pdma_get_drcmr(struct mmp_pdma_device *pdev, u32 drcmr) +{ + if (drcmr < DRCMR_REQ_LIMIT) + return DRCMR_BASE + (drcmr << 2); + return pdev->ops->drcmr_ext_base + ((drcmr - DRCMR_REQ_LIMIT) << 2); +} + /* For 32-bit PDMA */ static void write_next_addr_32(struct mmp_pdma_phy *phy, dma_addr_t addr) { @@ -301,7 +312,7 @@ static void enable_chan(struct mmp_pdma_phy *phy) pdev = to_mmp_pdma_dev(phy->vchan->chan.device); - reg = DRCMR(phy->vchan->drcmr); + reg = mmp_pdma_get_drcmr(pdev, phy->vchan->drcmr); writel(DRCMR_MAPVLD | phy->idx, phy->base + reg); dalgn = readl(phy->base + DALGN); @@ -437,7 +448,7 @@ static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan) return; /* clear the channel mapping in DRCMR */ - reg = DRCMR(pchan->drcmr); + reg = mmp_pdma_get_drcmr(pdev, pchan->drcmr); writel(0, pchan->phy->base + reg); spin_lock_irqsave(&pdev->phy_lock, flags); @@ -1179,6 +1190,7 @@ static const struct mmp_pdma_ops marvell_pdma_v1_ops = { .get_desc_dst_addr = get_desc_dst_addr_32, .run_bits = (DCSR_RUN), .dma_width = 32, + .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { @@ -1192,6 +1204,7 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { .get_desc_dst_addr = get_desc_dst_addr_64, .run_bits = (DCSR_RUN | DCSR_LPAEEN), .dma_width = 64, + .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; static const struct of_device_id mmp_pdma_dt_ids[] = { From 9d5f9bb105a34d513061843294936c759dc0934c Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:43 +0800 Subject: [PATCH 029/521] UPSTREAM: dmaengine: mmp_pdma: add SpacemiT K3 support SpacemiT K3 reuses most of the PDMA IP design found on K1, with one difference being the extended DRCMR base address. Add "spacemit,k3-pdma" compatible string and define a new mmp_pdma_ops for K3 PDMA. Signed-off-by: Guodong Xu Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-3-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul (cherry picked from commit 6587b8661a0b61c2f4b260bfc9f0e9ef9de0ea2e) Signed-off-by: Han Gao --- drivers/dma/mmp_pdma.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 6112369006eed3..386e85cd4882a3 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -52,6 +52,7 @@ #define DCSR_EORINTR BIT(9) /* The end of Receive */ #define DRCMR_BASE 0x0100 +#define DRCMR_EXT_BASE_K3 0x1000 #define DRCMR_EXT_BASE_DEFAULT 0x1100 #define DRCMR_REQ_LIMIT 64 #define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */ @@ -1207,6 +1208,20 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; +static const struct mmp_pdma_ops spacemit_k3_pdma_ops = { + .write_next_addr = write_next_addr_64, + .read_src_addr = read_src_addr_64, + .read_dst_addr = read_dst_addr_64, + .set_desc_next_addr = set_desc_next_addr_64, + .set_desc_src_addr = set_desc_src_addr_64, + .set_desc_dst_addr = set_desc_dst_addr_64, + .get_desc_src_addr = get_desc_src_addr_64, + .get_desc_dst_addr = get_desc_dst_addr_64, + .run_bits = (DCSR_RUN | DCSR_LPAEEN | DCSR_EORIRQEN | DCSR_EORSTOPEN), + .dma_width = 64, + .drcmr_ext_base = DRCMR_EXT_BASE_K3, +}; + static const struct of_device_id mmp_pdma_dt_ids[] = { { .compatible = "marvell,pdma-1.0", @@ -1214,6 +1229,9 @@ static const struct of_device_id mmp_pdma_dt_ids[] = { }, { .compatible = "spacemit,k1-pdma", .data = &spacemit_k1_pdma_ops + }, { + .compatible = "spacemit,k3-pdma", + .data = &spacemit_k3_pdma_ops }, { /* sentinel */ } From f885dfb28a3c24b6603bdb3fec368cad757810df Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 18 May 2026 11:32:44 +0800 Subject: [PATCH 030/521] UPSTREAM: riscv: dts: spacemit: Add PDMA controller node for K3 SoC Add the Peripheral DMA (PDMA) controller node for the SpacemiT K3 SoC. The PDMA controller provides general-purpose DMA capabilities for various peripheral devices across the system to offload CPU data transfers. Unlike the previous K1 SoC, where some DMA masters had memory addressing limitations (e.g. restricted to the 0-4GB space) requiring a dedicated dma-bus with dma-ranges to restrict memory allocations, the K3 DMA masters have full memory addressing capabilities. Therefore, the PDMA node is now instantiated directly under the main soc bus. Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-4-67fdf319a8f8@linux.spacemit.com Signed-off-by: Yixun Lan (cherry picked from commit 3f47ca8bb3c3f4a71688451a0cb7350d3e1e1059) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 815debd16409be..01124ff429a2f8 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -555,6 +555,17 @@ }; }; + pdma: dma-controller@d4000000 { + compatible = "spacemit,k3-pdma"; + reg = <0x0 0xd4000000 0x0 0x4000>; + clocks = <&syscon_apmu CLK_APMU_DMA>; + resets = <&syscon_apmu RESET_APMU_DMA>; + interrupts = <72 IRQ_TYPE_LEVEL_HIGH>; + dma-channels = <16>; + #dma-cells = <1>; + status = "disabled"; + }; + i2c0: i2c@d4010800 { compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; reg = <0x0 0xd4010800 0x0 0x38>; From 7b7e3add624b92ae9b60eaae4a23de60e31c6e3d Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 1 Apr 2026 01:12:47 +0800 Subject: [PATCH 031/521] UPSTREAM: dt-bindings: PCI: sophgo: Add dma-coherent property for SG2042 Add dma-coherent as an allowed property in the SG2042 PCIe host controller binding. SG2042's PCIe Root Complexes are cache-coherent with the CPU. Signed-off-by: Han Gao Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260331171248.973014-2-gaohan@iscas.ac.cn (cherry picked from commit c6d51515ee0b79a52b7e532b6b20067fa0cec96f) Signed-off-by: Han Gao --- .../devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml b/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml index f8b7ca57fff14c..ab482488b04753 100644 --- a/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml @@ -30,6 +30,8 @@ properties: device-id: const: 0x2042 + dma-coherent: true + msi-parent: true allOf: @@ -60,5 +62,6 @@ examples: vendor-id = <0x1f1c>; device-id = <0x2042>; cdns,no-bar-match-nbits = <48>; + dma-coherent; msi-parent = <&msi>; }; From 0cfbdd76a0b54f225a983d19daaf26e2f519d44d Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sun, 26 Apr 2026 09:34:48 +0800 Subject: [PATCH 032/521] UPSTREAM: riscv: dts: sophgo: sg2044: use hex for CPU unit address Previous the CPU unit address cpu of sg2044 use decimal, it is not following the general convention for unit addresses of the OF. Convent the unit address to hex to resolve this problem. The introduces a small change for the CPU node name, but it should nothing since there is no direct full-path reference to these CPU nodes. Fixes: 967a94a92aaa ("riscv: dts: add initial Sophgo SG2042 SoC device tree") Reviewed-by: Chen Wang Reviewed-by: Guo Ren Link: https://patch.msgid.link/20260426013449.694435-2-inochiama@gmail.com Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang (cherry picked from commit 207cbc477406a72952e27ace2eadbae55164f129) Signed-off-by: Han Gao --- arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi | 236 ++++++++++---------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi index 3135409c21492f..f66a382c95bd9c 100644 --- a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi @@ -14,7 +14,7 @@ cpu0: cpu@0 { compatible = "thead,c920", "riscv"; - reg = <0>; + reg = <0x0>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -50,7 +50,7 @@ cpu1: cpu@1 { compatible = "thead,c920", "riscv"; - reg = <1>; + reg = <0x1>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -86,7 +86,7 @@ cpu2: cpu@2 { compatible = "thead,c920", "riscv"; - reg = <2>; + reg = <0x2>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -122,7 +122,7 @@ cpu3: cpu@3 { compatible = "thead,c920", "riscv"; - reg = <3>; + reg = <0x3>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -158,7 +158,7 @@ cpu4: cpu@4 { compatible = "thead,c920", "riscv"; - reg = <4>; + reg = <0x4>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -194,7 +194,7 @@ cpu5: cpu@5 { compatible = "thead,c920", "riscv"; - reg = <5>; + reg = <0x5>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -230,7 +230,7 @@ cpu6: cpu@6 { compatible = "thead,c920", "riscv"; - reg = <6>; + reg = <0x6>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -266,7 +266,7 @@ cpu7: cpu@7 { compatible = "thead,c920", "riscv"; - reg = <7>; + reg = <0x7>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -302,7 +302,7 @@ cpu8: cpu@8 { compatible = "thead,c920", "riscv"; - reg = <8>; + reg = <0x8>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -338,7 +338,7 @@ cpu9: cpu@9 { compatible = "thead,c920", "riscv"; - reg = <9>; + reg = <0x9>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -372,9 +372,9 @@ }; }; - cpu10: cpu@10 { + cpu10: cpu@a { compatible = "thead,c920", "riscv"; - reg = <10>; + reg = <0xa>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -408,9 +408,9 @@ }; }; - cpu11: cpu@11 { + cpu11: cpu@b { compatible = "thead,c920", "riscv"; - reg = <11>; + reg = <0xb>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -444,9 +444,9 @@ }; }; - cpu12: cpu@12 { + cpu12: cpu@c { compatible = "thead,c920", "riscv"; - reg = <12>; + reg = <0xc>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -480,9 +480,9 @@ }; }; - cpu13: cpu@13 { + cpu13: cpu@d { compatible = "thead,c920", "riscv"; - reg = <13>; + reg = <0xd>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -516,9 +516,9 @@ }; }; - cpu14: cpu@14 { + cpu14: cpu@e { compatible = "thead,c920", "riscv"; - reg = <14>; + reg = <0xe>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -552,9 +552,9 @@ }; }; - cpu15: cpu@15 { + cpu15: cpu@f { compatible = "thead,c920", "riscv"; - reg = <15>; + reg = <0xf>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -588,9 +588,9 @@ }; }; - cpu16: cpu@16 { + cpu16: cpu@10 { compatible = "thead,c920", "riscv"; - reg = <16>; + reg = <0x10>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -624,9 +624,9 @@ }; }; - cpu17: cpu@17 { + cpu17: cpu@11 { compatible = "thead,c920", "riscv"; - reg = <17>; + reg = <0x11>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -660,9 +660,9 @@ }; }; - cpu18: cpu@18 { + cpu18: cpu@12 { compatible = "thead,c920", "riscv"; - reg = <18>; + reg = <0x12>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -696,9 +696,9 @@ }; }; - cpu19: cpu@19 { + cpu19: cpu@13 { compatible = "thead,c920", "riscv"; - reg = <19>; + reg = <0x13>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -732,9 +732,9 @@ }; }; - cpu20: cpu@20 { + cpu20: cpu@14 { compatible = "thead,c920", "riscv"; - reg = <20>; + reg = <0x14>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -768,9 +768,9 @@ }; }; - cpu21: cpu@21 { + cpu21: cpu@15 { compatible = "thead,c920", "riscv"; - reg = <21>; + reg = <0x15>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -804,9 +804,9 @@ }; }; - cpu22: cpu@22 { + cpu22: cpu@16 { compatible = "thead,c920", "riscv"; - reg = <22>; + reg = <0x16>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -840,9 +840,9 @@ }; }; - cpu23: cpu@23 { + cpu23: cpu@17 { compatible = "thead,c920", "riscv"; - reg = <23>; + reg = <0x17>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -876,9 +876,9 @@ }; }; - cpu24: cpu@24 { + cpu24: cpu@18 { compatible = "thead,c920", "riscv"; - reg = <24>; + reg = <0x18>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -912,9 +912,9 @@ }; }; - cpu25: cpu@25 { + cpu25: cpu@19 { compatible = "thead,c920", "riscv"; - reg = <25>; + reg = <0x19>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -948,9 +948,9 @@ }; }; - cpu26: cpu@26 { + cpu26: cpu@1a { compatible = "thead,c920", "riscv"; - reg = <26>; + reg = <0x1a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -984,9 +984,9 @@ }; }; - cpu27: cpu@27 { + cpu27: cpu@1b { compatible = "thead,c920", "riscv"; - reg = <27>; + reg = <0x1b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1020,9 +1020,9 @@ }; }; - cpu28: cpu@28 { + cpu28: cpu@1c { compatible = "thead,c920", "riscv"; - reg = <28>; + reg = <0x1c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1056,9 +1056,9 @@ }; }; - cpu29: cpu@29 { + cpu29: cpu@1d { compatible = "thead,c920", "riscv"; - reg = <29>; + reg = <0x1d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1092,9 +1092,9 @@ }; }; - cpu30: cpu@30 { + cpu30: cpu@1e { compatible = "thead,c920", "riscv"; - reg = <30>; + reg = <0x1e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1128,9 +1128,9 @@ }; }; - cpu31: cpu@31 { + cpu31: cpu@1f { compatible = "thead,c920", "riscv"; - reg = <31>; + reg = <0x1f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1164,9 +1164,9 @@ }; }; - cpu32: cpu@32 { + cpu32: cpu@20 { compatible = "thead,c920", "riscv"; - reg = <32>; + reg = <0x20>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1200,9 +1200,9 @@ }; }; - cpu33: cpu@33 { + cpu33: cpu@21 { compatible = "thead,c920", "riscv"; - reg = <33>; + reg = <0x21>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1236,9 +1236,9 @@ }; }; - cpu34: cpu@34 { + cpu34: cpu@22 { compatible = "thead,c920", "riscv"; - reg = <34>; + reg = <0x22>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1272,9 +1272,9 @@ }; }; - cpu35: cpu@35 { + cpu35: cpu@23 { compatible = "thead,c920", "riscv"; - reg = <35>; + reg = <0x23>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1308,9 +1308,9 @@ }; }; - cpu36: cpu@36 { + cpu36: cpu@24 { compatible = "thead,c920", "riscv"; - reg = <36>; + reg = <0x24>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1344,9 +1344,9 @@ }; }; - cpu37: cpu@37 { + cpu37: cpu@25 { compatible = "thead,c920", "riscv"; - reg = <37>; + reg = <0x25>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1380,9 +1380,9 @@ }; }; - cpu38: cpu@38 { + cpu38: cpu@26 { compatible = "thead,c920", "riscv"; - reg = <38>; + reg = <0x26>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1416,9 +1416,9 @@ }; }; - cpu39: cpu@39 { + cpu39: cpu@27 { compatible = "thead,c920", "riscv"; - reg = <39>; + reg = <0x27>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1452,9 +1452,9 @@ }; }; - cpu40: cpu@40 { + cpu40: cpu@28 { compatible = "thead,c920", "riscv"; - reg = <40>; + reg = <0x28>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1488,9 +1488,9 @@ }; }; - cpu41: cpu@41 { + cpu41: cpu@29 { compatible = "thead,c920", "riscv"; - reg = <41>; + reg = <0x29>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1524,9 +1524,9 @@ }; }; - cpu42: cpu@42 { + cpu42: cpu@2a { compatible = "thead,c920", "riscv"; - reg = <42>; + reg = <0x2a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1560,9 +1560,9 @@ }; }; - cpu43: cpu@43 { + cpu43: cpu@2b { compatible = "thead,c920", "riscv"; - reg = <43>; + reg = <0x2b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1596,9 +1596,9 @@ }; }; - cpu44: cpu@44 { + cpu44: cpu@2c { compatible = "thead,c920", "riscv"; - reg = <44>; + reg = <0x2c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1632,9 +1632,9 @@ }; }; - cpu45: cpu@45 { + cpu45: cpu@2d { compatible = "thead,c920", "riscv"; - reg = <45>; + reg = <0x2d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1668,9 +1668,9 @@ }; }; - cpu46: cpu@46 { + cpu46: cpu@2e { compatible = "thead,c920", "riscv"; - reg = <46>; + reg = <0x2e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1704,9 +1704,9 @@ }; }; - cpu47: cpu@47 { + cpu47: cpu@2f { compatible = "thead,c920", "riscv"; - reg = <47>; + reg = <0x2f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1740,9 +1740,9 @@ }; }; - cpu48: cpu@48 { + cpu48: cpu@30 { compatible = "thead,c920", "riscv"; - reg = <48>; + reg = <0x30>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1776,9 +1776,9 @@ }; }; - cpu49: cpu@49 { + cpu49: cpu@31 { compatible = "thead,c920", "riscv"; - reg = <49>; + reg = <0x31>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1812,9 +1812,9 @@ }; }; - cpu50: cpu@50 { + cpu50: cpu@32 { compatible = "thead,c920", "riscv"; - reg = <50>; + reg = <0x32>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1848,9 +1848,9 @@ }; }; - cpu51: cpu@51 { + cpu51: cpu@33 { compatible = "thead,c920", "riscv"; - reg = <51>; + reg = <0x33>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1884,9 +1884,9 @@ }; }; - cpu52: cpu@52 { + cpu52: cpu@34 { compatible = "thead,c920", "riscv"; - reg = <52>; + reg = <0x34>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1920,9 +1920,9 @@ }; }; - cpu53: cpu@53 { + cpu53: cpu@35 { compatible = "thead,c920", "riscv"; - reg = <53>; + reg = <0x35>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1956,9 +1956,9 @@ }; }; - cpu54: cpu@54 { + cpu54: cpu@36 { compatible = "thead,c920", "riscv"; - reg = <54>; + reg = <0x36>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1992,9 +1992,9 @@ }; }; - cpu55: cpu@55 { + cpu55: cpu@37 { compatible = "thead,c920", "riscv"; - reg = <55>; + reg = <0x37>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2028,9 +2028,9 @@ }; }; - cpu56: cpu@56 { + cpu56: cpu@38 { compatible = "thead,c920", "riscv"; - reg = <56>; + reg = <0x38>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2064,9 +2064,9 @@ }; }; - cpu57: cpu@57 { + cpu57: cpu@39 { compatible = "thead,c920", "riscv"; - reg = <57>; + reg = <0x39>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2100,9 +2100,9 @@ }; }; - cpu58: cpu@58 { + cpu58: cpu@3a { compatible = "thead,c920", "riscv"; - reg = <58>; + reg = <0x3a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2136,9 +2136,9 @@ }; }; - cpu59: cpu@59 { + cpu59: cpu@3b { compatible = "thead,c920", "riscv"; - reg = <59>; + reg = <0x3b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2172,9 +2172,9 @@ }; }; - cpu60: cpu@60 { + cpu60: cpu@3c { compatible = "thead,c920", "riscv"; - reg = <60>; + reg = <0x3c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2208,9 +2208,9 @@ }; }; - cpu61: cpu@61 { + cpu61: cpu@3d { compatible = "thead,c920", "riscv"; - reg = <61>; + reg = <0x3d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2244,9 +2244,9 @@ }; }; - cpu62: cpu@62 { + cpu62: cpu@3e { compatible = "thead,c920", "riscv"; - reg = <62>; + reg = <0x3e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2280,9 +2280,9 @@ }; }; - cpu63: cpu@63 { + cpu63: cpu@3f { compatible = "thead,c920", "riscv"; - reg = <63>; + reg = <0x3f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; From d0ef912729229773fdb04d89f8835a14138fac56 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sun, 26 Apr 2026 09:34:49 +0800 Subject: [PATCH 033/521] UPSTREAM: riscv: dts: sophgo: sg2042: use hex for CPU unit address Previous the CPU unit address cpu of sg2042 use decimal, it is not following the general convention for unit addresses of the OF. Convent the unit address to hex to resolve this problem. The introduces a small change for the CPU node name, but it should affect nothing since there is no direct full-path reference to these CPU nodes. Fixes: ae5bac370ed4 ("riscv: dts: sophgo: Add initial device tree of Sophgo SRD3-10") Tested-by: Chen Wang # Pioneerbox. Reviewed-by: Guo Ren Reviewed-by: Chen Wang Acked-by: Conor Dooley Tested-by: Chen Wang on Pioneerbox. Link: https://patch.msgid.link/20260426013449.694435-3-inochiama@gmail.com Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang (cherry picked from commit a7e658907686528fe06a11828b04a3e42df9ef18) Signed-off-by: Han Gao --- arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi | 236 ++++++++++---------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi index 509488eee4321c..fd8906b313d2eb 100644 --- a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi @@ -263,7 +263,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <0>; + reg = <0x0>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -291,7 +291,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <1>; + reg = <0x1>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -319,7 +319,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <2>; + reg = <0x2>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -347,7 +347,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <3>; + reg = <0x3>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -375,7 +375,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <4>; + reg = <0x4>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -403,7 +403,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <5>; + reg = <0x5>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -431,7 +431,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <6>; + reg = <0x6>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -459,7 +459,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <7>; + reg = <0x7>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -487,7 +487,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <8>; + reg = <0x8>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -515,7 +515,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <9>; + reg = <0x9>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -533,7 +533,7 @@ }; }; - cpu10: cpu@10 { + cpu10: cpu@a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -543,7 +543,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <10>; + reg = <0xa>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -561,7 +561,7 @@ }; }; - cpu11: cpu@11 { + cpu11: cpu@b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -571,7 +571,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <11>; + reg = <0xb>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -589,7 +589,7 @@ }; }; - cpu12: cpu@12 { + cpu12: cpu@c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -599,7 +599,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <12>; + reg = <0xc>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -617,7 +617,7 @@ }; }; - cpu13: cpu@13 { + cpu13: cpu@d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -627,7 +627,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <13>; + reg = <0xd>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -645,7 +645,7 @@ }; }; - cpu14: cpu@14 { + cpu14: cpu@e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -655,7 +655,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <14>; + reg = <0xe>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -673,7 +673,7 @@ }; }; - cpu15: cpu@15 { + cpu15: cpu@f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -683,7 +683,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <15>; + reg = <0xf>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -701,7 +701,7 @@ }; }; - cpu16: cpu@16 { + cpu16: cpu@10 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -711,7 +711,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <16>; + reg = <0x10>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -729,7 +729,7 @@ }; }; - cpu17: cpu@17 { + cpu17: cpu@11 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -739,7 +739,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <17>; + reg = <0x11>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -757,7 +757,7 @@ }; }; - cpu18: cpu@18 { + cpu18: cpu@12 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -767,7 +767,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <18>; + reg = <0x12>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -785,7 +785,7 @@ }; }; - cpu19: cpu@19 { + cpu19: cpu@13 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -795,7 +795,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <19>; + reg = <0x13>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -813,7 +813,7 @@ }; }; - cpu20: cpu@20 { + cpu20: cpu@14 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -823,7 +823,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <20>; + reg = <0x14>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -841,7 +841,7 @@ }; }; - cpu21: cpu@21 { + cpu21: cpu@15 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -851,7 +851,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <21>; + reg = <0x15>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -869,7 +869,7 @@ }; }; - cpu22: cpu@22 { + cpu22: cpu@16 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -879,7 +879,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <22>; + reg = <0x16>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -897,7 +897,7 @@ }; }; - cpu23: cpu@23 { + cpu23: cpu@17 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -907,7 +907,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <23>; + reg = <0x17>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -925,7 +925,7 @@ }; }; - cpu24: cpu@24 { + cpu24: cpu@18 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -935,7 +935,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <24>; + reg = <0x18>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -953,7 +953,7 @@ }; }; - cpu25: cpu@25 { + cpu25: cpu@19 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -963,7 +963,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <25>; + reg = <0x19>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -981,7 +981,7 @@ }; }; - cpu26: cpu@26 { + cpu26: cpu@1a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -991,7 +991,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <26>; + reg = <0x1a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1009,7 +1009,7 @@ }; }; - cpu27: cpu@27 { + cpu27: cpu@1b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1019,7 +1019,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <27>; + reg = <0x1b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1037,7 +1037,7 @@ }; }; - cpu28: cpu@28 { + cpu28: cpu@1c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1047,7 +1047,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <28>; + reg = <0x1c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1065,7 +1065,7 @@ }; }; - cpu29: cpu@29 { + cpu29: cpu@1d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1075,7 +1075,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <29>; + reg = <0x1d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1093,7 +1093,7 @@ }; }; - cpu30: cpu@30 { + cpu30: cpu@1e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1103,7 +1103,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <30>; + reg = <0x1e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1121,7 +1121,7 @@ }; }; - cpu31: cpu@31 { + cpu31: cpu@1f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1131,7 +1131,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <31>; + reg = <0x1f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1149,7 +1149,7 @@ }; }; - cpu32: cpu@32 { + cpu32: cpu@20 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1159,7 +1159,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <32>; + reg = <0x20>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1177,7 +1177,7 @@ }; }; - cpu33: cpu@33 { + cpu33: cpu@21 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1187,7 +1187,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <33>; + reg = <0x21>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1205,7 +1205,7 @@ }; }; - cpu34: cpu@34 { + cpu34: cpu@22 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1215,7 +1215,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <34>; + reg = <0x22>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1233,7 +1233,7 @@ }; }; - cpu35: cpu@35 { + cpu35: cpu@23 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1243,7 +1243,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <35>; + reg = <0x23>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1261,7 +1261,7 @@ }; }; - cpu36: cpu@36 { + cpu36: cpu@24 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1271,7 +1271,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <36>; + reg = <0x24>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1289,7 +1289,7 @@ }; }; - cpu37: cpu@37 { + cpu37: cpu@25 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1299,7 +1299,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <37>; + reg = <0x25>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1317,7 +1317,7 @@ }; }; - cpu38: cpu@38 { + cpu38: cpu@26 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1327,7 +1327,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <38>; + reg = <0x26>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1345,7 +1345,7 @@ }; }; - cpu39: cpu@39 { + cpu39: cpu@27 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1355,7 +1355,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <39>; + reg = <0x27>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1373,7 +1373,7 @@ }; }; - cpu40: cpu@40 { + cpu40: cpu@28 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1383,7 +1383,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <40>; + reg = <0x28>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1401,7 +1401,7 @@ }; }; - cpu41: cpu@41 { + cpu41: cpu@29 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1411,7 +1411,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <41>; + reg = <0x29>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1429,7 +1429,7 @@ }; }; - cpu42: cpu@42 { + cpu42: cpu@2a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1439,7 +1439,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <42>; + reg = <0x2a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1457,7 +1457,7 @@ }; }; - cpu43: cpu@43 { + cpu43: cpu@2b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1467,7 +1467,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <43>; + reg = <0x2b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1485,7 +1485,7 @@ }; }; - cpu44: cpu@44 { + cpu44: cpu@2c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1495,7 +1495,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <44>; + reg = <0x2c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1513,7 +1513,7 @@ }; }; - cpu45: cpu@45 { + cpu45: cpu@2d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1523,7 +1523,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <45>; + reg = <0x2d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1541,7 +1541,7 @@ }; }; - cpu46: cpu@46 { + cpu46: cpu@2e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1551,7 +1551,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <46>; + reg = <0x2e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1569,7 +1569,7 @@ }; }; - cpu47: cpu@47 { + cpu47: cpu@2f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1579,7 +1579,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <47>; + reg = <0x2f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1597,7 +1597,7 @@ }; }; - cpu48: cpu@48 { + cpu48: cpu@30 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1607,7 +1607,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <48>; + reg = <0x30>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1625,7 +1625,7 @@ }; }; - cpu49: cpu@49 { + cpu49: cpu@31 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1635,7 +1635,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <49>; + reg = <0x31>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1653,7 +1653,7 @@ }; }; - cpu50: cpu@50 { + cpu50: cpu@32 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1663,7 +1663,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <50>; + reg = <0x32>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1681,7 +1681,7 @@ }; }; - cpu51: cpu@51 { + cpu51: cpu@33 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1691,7 +1691,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <51>; + reg = <0x33>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1709,7 +1709,7 @@ }; }; - cpu52: cpu@52 { + cpu52: cpu@34 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1719,7 +1719,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <52>; + reg = <0x34>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1737,7 +1737,7 @@ }; }; - cpu53: cpu@53 { + cpu53: cpu@35 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1747,7 +1747,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <53>; + reg = <0x35>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1765,7 +1765,7 @@ }; }; - cpu54: cpu@54 { + cpu54: cpu@36 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1775,7 +1775,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <54>; + reg = <0x36>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1793,7 +1793,7 @@ }; }; - cpu55: cpu@55 { + cpu55: cpu@37 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1803,7 +1803,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <55>; + reg = <0x37>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1821,7 +1821,7 @@ }; }; - cpu56: cpu@56 { + cpu56: cpu@38 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1831,7 +1831,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <56>; + reg = <0x38>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1849,7 +1849,7 @@ }; }; - cpu57: cpu@57 { + cpu57: cpu@39 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1859,7 +1859,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <57>; + reg = <0x39>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1877,7 +1877,7 @@ }; }; - cpu58: cpu@58 { + cpu58: cpu@3a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1887,7 +1887,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <58>; + reg = <0x3a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1905,7 +1905,7 @@ }; }; - cpu59: cpu@59 { + cpu59: cpu@3b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1915,7 +1915,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <59>; + reg = <0x3b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1933,7 +1933,7 @@ }; }; - cpu60: cpu@60 { + cpu60: cpu@3c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1943,7 +1943,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <60>; + reg = <0x3c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1961,7 +1961,7 @@ }; }; - cpu61: cpu@61 { + cpu61: cpu@3d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1971,7 +1971,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <61>; + reg = <0x3d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1989,7 +1989,7 @@ }; }; - cpu62: cpu@62 { + cpu62: cpu@3e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1999,7 +1999,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <62>; + reg = <0x3e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2017,7 +2017,7 @@ }; }; - cpu63: cpu@63 { + cpu63: cpu@3f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -2027,7 +2027,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <63>; + reg = <0x3f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; From 232038e4817c069f7d6ae297047422493a726212 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 8 Apr 2026 00:01:43 +0800 Subject: [PATCH 034/521] UPSTREAM: riscv: dts: sophgo: reduce SG2042 MSI count to 16 The SG2042 MSI controller has one 32-bit doorbell register, and each bit corresponds to an interrupt. At a glance, it seems that the MSI controller can support 32 interrupts; however the PCI MSI capability only supports 16-bit messages, which makes the high 16 interrupts unusable in such way. Reduce the MSI count to 16 to prevent producing MSI message values that cannot fit 16-bit integers. Signed-off-by: Icenowy Zheng Reviewed-by: Chen Wang Tested-by: Chen Wang on Pioneerbox. Link: https://patch.msgid.link/20260407160143.1182430-1-zhengxingda@iscas.ac.cn Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang (cherry picked from commit 903a9364e40563faf4730dc63ad7446246f494ff) Signed-off-by: Han Gao --- arch/riscv/boot/dts/sophgo/sg2042.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/sophgo/sg2042.dtsi b/arch/riscv/boot/dts/sophgo/sg2042.dtsi index 3af77054974262..7eab0655f15002 100644 --- a/arch/riscv/boot/dts/sophgo/sg2042.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2042.dtsi @@ -234,7 +234,7 @@ reg-names = "clr", "doorbell"; msi-controller; #msi-cells = <0>; - msi-ranges = <&intc 64 IRQ_TYPE_EDGE_RISING 32>; + msi-ranges = <&intc 64 IRQ_TYPE_EDGE_RISING 16>; }; rpgate: clock-controller@7030010368 { From 4ce8fd44a44f0761b9290371a3c1fd958dff86be Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 28 Apr 2026 10:46:50 +0000 Subject: [PATCH 035/521] UPSTREAM: dt-bindings: pwm: marvell,pxa-pwm: Add SpacemiT K3 PWM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PWM controller in SpacemiT K3 SoC reuse the same IP as previous K1 generation, while the difference is that one additional bus clock is added. Signed-off-by: Yixun Lan Acked-by: Conor Dooley Link: https://patch.msgid.link/20260428-03-k3-pwm-drv-v2-1-a532bbe45556@kernel.org Signed-off-by: Uwe Kleine-König (cherry picked from commit e7abbff5e8b2924a53b583e6d57804c6cad0ab81) Signed-off-by: Han Gao --- .../bindings/pwm/marvell,pxa-pwm.yaml | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml index 8df327e5281075..f1422a401b6ba7 100644 --- a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml @@ -15,7 +15,9 @@ allOf: properties: compatible: contains: - const: spacemit,k1-pwm + enum: + - spacemit,k1-pwm + - spacemit,k3-pwm then: properties: "#pwm-cells": @@ -26,6 +28,26 @@ allOf: const: 1 description: | Used for specifying the period length in nanoseconds. + - if: + properties: + compatible: + contains: + enum: + - spacemit,k3-pwm + then: + required: + - clock-names + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + else: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 properties: compatible: @@ -36,7 +58,9 @@ properties: - marvell,pxa168-pwm - marvell,pxa910-pwm - items: - - const: spacemit,k1-pwm + - enum: + - spacemit,k1-pwm + - spacemit,k3-pwm - const: marvell,pxa910-pwm reg: @@ -47,7 +71,18 @@ properties: description: Number of cells in a pwm specifier. clocks: - maxItems: 1 + minItems: 1 + items: + - description: The function clock + - description: An optional bus clock + + clock-names: + minItems: 1 + maxItems: 2 + oneOf: + - items: + - const: func + - const: bus resets: maxItems: 1 From 30912e1b81c5bfef42536d2d41c1227573e554cc Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 28 Apr 2026 10:46:51 +0000 Subject: [PATCH 036/521] UPSTREAM: pwm: pxa: Add optional bus clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add one secondary optional bus clock for the PWM PXA driver, also keep it compatible with old single clock. The SpacemiT K3 SoC require a bus clock for PWM controller, acquire and enable it during probe phase. Signed-off-by: Yixun Lan Link: https://patch.msgid.link/20260428-03-k3-pwm-drv-v2-2-a532bbe45556@kernel.org Signed-off-by: Uwe Kleine-König (cherry picked from commit 74bca0f2d37f5fd9a4bd5378d9f9579e72038556) Signed-off-by: Han Gao --- drivers/pwm/pwm-pxa.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 0f5bdb0e395ea0..80d2fa10919f86 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -161,6 +161,7 @@ static int pwm_probe(struct platform_device *pdev) const struct platform_device_id *id = platform_get_device_id(pdev); struct pwm_chip *chip; struct pxa_pwm_chip *pc; + struct clk *bus_clk; struct device *dev = &pdev->dev; struct reset_control *rst; int ret = 0; @@ -177,7 +178,12 @@ static int pwm_probe(struct platform_device *pdev) return PTR_ERR(chip); pc = to_pxa_pwm_chip(chip); - pc->clk = devm_clk_get(dev, NULL); + bus_clk = devm_clk_get_optional_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) + return dev_err_probe(dev, PTR_ERR(bus_clk), "Failed to get bus clock\n"); + + /* Get named func clk if bus clock is valid */ + pc->clk = devm_clk_get(dev, bus_clk ? "func" : NULL); if (IS_ERR(pc->clk)) return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n"); From 0d8d80ec736b1c0c24fadc0248299ab90469d3b1 Mon Sep 17 00:00:00 2001 From: Chen Pei Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 037/521] UPSTREAM: riscv: ftrace: select HAVE_BUILDTIME_MCOUNT_SORT RISC-V already satisfies all prerequisites for build-time mcount sorting: the sorttable host tool handles EM_RISCV in its machine-type dispatch, and the __mcount_loc section entries are stored as direct virtual addresses in the final vmlinux binary, so no relocation processing is required during the sort step. Select HAVE_BUILDTIME_MCOUNT_SORT so that BUILDTIME_MCOUNT_SORT is automatically enabled when DYNAMIC_FTRACE is configured. This allows sorttable to sort the __mcount_loc section at link time, making the run-time ftrace initialisation path skip the software sort and reducing kernel startup overhead. Verified with CONFIG_FTRACE_SORT_STARTUP_TEST=y, which confirms that the section produced by the build is already in ascending order: [ 0.000000] ftrace section at ffffffff81015a60 sorted properly Signed-off-by: Chen Pei Tested-by: Guo Ren Link: https://patch.msgid.link/20260409114736.907-1-cp0613@linux.alibaba.com Signed-off-by: Paul Walmsley (cherry picked from commit 9fd44698b11dbdf4d29fdef0f0da25f320c13854) Signed-off-by: Han Gao --- arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c5754942cf85a4..46a89225b9cbb3 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -151,6 +151,7 @@ config RISCV select HAVE_ARCH_USERFAULTFD_WP if 64BIT && MMU && USERFAULTFD && RISCV_ISA_SVRSW60T59B select HAVE_ARCH_VMAP_STACK if MMU && 64BIT select HAVE_ASM_MODVERSIONS + select HAVE_BUILDTIME_MCOUNT_SORT select HAVE_CONTEXT_TRACKING_USER select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS if MMU From b638c128d81e788aaf7b3317dff45796bdc5887e Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 30 Apr 2026 19:52:12 +0100 Subject: [PATCH 038/521] UPSTREAM: dts: spacemit: set console baud rate on bpif3 Because the default console's baud rate is not set, defconfig kernels do not have any serial output on this platform. Set the baud rate to 115200, matching what is used by U-Boot etc on this platform. Suggested-by: Vivian Wang Fixes: d60d57ab6b2a8 ("riscv: dts: spacemit: add Banana Pi BPI-F3 board device tree") Signed-off-by: Conor Dooley Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260430-reword-overstep-3be08b7eab25@spud Signed-off-by: Yixun Lan (cherry picked from commit 24c12ca43b12c104389d9a159207d0b25779d0af) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 9429189354d6aa..e20daa50a152f3 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -20,7 +20,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; leds { From 287aabdff9748868544679894e660f426e4df190 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Mon, 18 May 2026 18:00:30 +0800 Subject: [PATCH 039/521] UPSTREAM: riscv: dts: spacemit: enable USB3 on OrangePi R2S Enable the DWC3 USB3.0 controller and its associated PHY on the OrangePi R2S. The USB regulator provides VBUS for USB2 and USB3 ports, but the USB2 ports are handled by a separate controller. Signed-off-by: Chukun Pan Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260518100030.2354606-1-amadeus@jmu.edu.cn Signed-off-by: Yixun Lan (cherry picked from commit c5ad6737df88bd3c3e83145edb25737cb19f9a1f) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-orangepi-r2s.dts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts index de75f6aac74055..1ecc40749e5ac1 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts @@ -21,6 +21,19 @@ chosen { stdout-path = "serial0"; }; + + vcc5v0_usb: regulator-vcc5v0-usb { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio K1_GPIO(126) GPIO_ACTIVE_HIGH>; + regulator-name = "vcc5v0_usb"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; +}; + +&combo_phy { + status = "okay"; }; &emmc { @@ -90,3 +103,13 @@ pinctrl-0 = <&uart0_2_cfg>; status = "okay"; }; + +&usbphy2 { + status = "okay"; +}; + +&usb_dwc3 { + dr_mode = "host"; + vbus-supply = <&vcc5v0_usb>; + status = "okay"; +}; From 0d348e0e04a96f3cef5632b11da04f86ca6e7800 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 28 Apr 2026 10:57:29 +0000 Subject: [PATCH 040/521] UPSTREAM: dts: riscv: spacemit: correct 32k clock frequency The 32k oscillator's clock frequency is actually 32768Hz, so correct it. Fixes: 67072c8cd48c ("riscv: dts: spacemit: k3: add clock tree") Fixes: a6fafa64b03a ("riscv: dts: spacemit: Add clock tree for SpacemiT K1") Link: https://patch.msgid.link/20260428-06-k3-clk-osc32k-v1-1-e2378da7cb9b@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit 3c350f6284d8ea5e7a9648241b2e9604f2262d42) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1.dtsi | 2 +- arch/riscv/boot/dts/spacemit/k3.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index 2ed5cc24e505b2..cba160c5edc5d6 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -333,7 +333,7 @@ osc_32k: clock-32k { compatible = "fixed-clock"; - clock-frequency = <32000>; + clock-frequency = <32768>; clock-output-names = "osc_32k"; #clock-cells = <0>; }; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 01124ff429a2f8..9e3c005f7bdf9c 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -424,7 +424,7 @@ osc_32k: clock-32k { compatible = "fixed-clock"; - clock-frequency = <32000>; + clock-frequency = <32768>; clock-output-names = "osc_32k"; #clock-cells = <0>; }; From 6d2260d56e42f50ee10748625f5eae5e42e3ccab Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 22 May 2026 21:12:14 +0800 Subject: [PATCH 041/521] UPSTREAM: ASoC: dt-bindings: add SpacemiT K3 SoC compatible Add the spacemit,k3-i2s compatible string for the K3 SoC I2S controller. The K3 I2S IP is the same as K1 but requires additional clocks: a dedicated sysclk_div clock, along with common_sysclk and common_bclk which are shared across multiple I2S controllers on K3. Acked-by: Rob Herring (Arm) Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260522-k3-i2s-v3-1-08e3eb811d93@linux.spacemit.com Signed-off-by: Mark Brown (cherry picked from commit 6bc6b28c031484092ce1f829f59e4768e12b268e) Signed-off-by: Han Gao --- .../bindings/sound/spacemit,k1-i2s.yaml | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml index 55bd0b307d22b3..240d90402e4f95 100644 --- a/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/sound/spacemit,k1-i2s.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: K1 I2S controller +title: SpacemiT K1/K3 I2S controller description: The I2S bus (Inter-IC sound bus) is a serial link for digital @@ -15,27 +15,54 @@ maintainers: allOf: - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + const: spacemit,k3-i2s + then: + properties: + clocks: + minItems: 7 + clock-names: + minItems: 7 + else: + properties: + clocks: + maxItems: 4 + clock-names: + maxItems: 4 properties: compatible: - const: spacemit,k1-i2s + enum: + - spacemit,k1-i2s + - spacemit,k3-i2s reg: maxItems: 1 clocks: + minItems: 4 items: - description: clock for I2S sysclk - description: clock for I2S bclk - description: clock for I2S bus - description: clock for I2S controller + - description: clock for I2S sysclk divider + - description: clock for I2S common sysclk + - description: clock for I2S common bclk clock-names: + minItems: 4 items: - const: sysclk - const: bclk - const: bus - const: func + - const: sysclk_div + - const: c_sysclk + - const: c_bclk dmas: minItems: 1 From 93587dddfb35890f920bed4a653ffa930a976586 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 22 May 2026 21:12:15 +0800 Subject: [PATCH 042/521] UPSTREAM: ASoC: spacemit: add K3 SoC support with additional clocks Add support for the SpacemiT K3 SoC I2S controller, which shares the same IP as K1 but requires additional clocks: sysclk_div, c_sysclk, and c_bclk. These clocks only exist on K3 and are not present on K1. The sysclk_div clock is present on most K3 I2S controllers except I2S1. The c_sysclk and c_bclk clocks are shared across multiple I2S controllers on K3. Use devm_clk_get_optional_enabled() to acquire these clocks so that the driver works on both K1 (where they are absent) and K3 without needing SoC-specific match data. For K3, the sysclk_div rate is set before sysclk in set_sysclk, and the common clock rates are configured in hw_params based on the sample rate. Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260522-k3-i2s-v3-2-08e3eb811d93@linux.spacemit.com Signed-off-by: Mark Brown (cherry picked from commit 1c6ca2ca5c1bdc97912cf3f9b4a1e186919a6a98) Signed-off-by: Han Gao --- sound/soc/spacemit/k1_i2s.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index 5420ca2aefbd87..8871fc15b29cc0 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -53,6 +53,9 @@ struct spacemit_i2s_dev { struct clk *sysclk; struct clk *bclk; struct clk *sspa_clk; + struct clk *sysclk_div; + struct clk *c_sysclk; + struct clk *c_bclk; struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; @@ -206,6 +209,14 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, params_rate(params) * data_bits; + ret = clk_set_rate(i2s->c_sysclk, bclk_rate * 2); + if (ret) + return ret; + + ret = clk_set_rate(i2s->c_bclk, bclk_rate); + if (ret) + return ret; + ret = clk_set_rate(i2s->bclk, bclk_rate); if (ret) return ret; @@ -217,10 +228,17 @@ static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { struct spacemit_i2s_dev *i2s = dev_get_drvdata(cpu_dai->dev); + int ret; if (freq == 0) return 0; + if (i2s->sysclk_div) { + ret = clk_set_rate(i2s->sysclk_div, freq); + if (ret) + return ret; + } + return clk_set_rate(i2s->sysclk, freq); } @@ -436,6 +454,21 @@ static int spacemit_i2s_probe(struct platform_device *pdev) return dev_err_probe(i2s->dev, PTR_ERR(i2s->sspa_clk), "failed to enable sspa clock\n"); + i2s->sysclk_div = devm_clk_get_optional_enabled(i2s->dev, "sysclk_div"); + if (IS_ERR(i2s->sysclk_div)) + return dev_err_probe(i2s->dev, PTR_ERR(i2s->sysclk_div), + "failed to enable sysclk_div clock\n"); + + i2s->c_sysclk = devm_clk_get_optional_enabled(i2s->dev, "c_sysclk"); + if (IS_ERR(i2s->c_sysclk)) + return dev_err_probe(i2s->dev, PTR_ERR(i2s->c_sysclk), + "failed to enable c_sysclk clock\n"); + + i2s->c_bclk = devm_clk_get_optional_enabled(i2s->dev, "c_bclk"); + if (IS_ERR(i2s->c_bclk)) + return dev_err_probe(i2s->dev, PTR_ERR(i2s->c_bclk), + "failed to enable c_bclk clock\n"); + i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2s->base)) return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); @@ -462,6 +495,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) static const struct of_device_id spacemit_i2s_of_match[] = { { .compatible = "spacemit,k1-i2s", }, + { .compatible = "spacemit,k3-i2s", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spacemit_i2s_of_match); @@ -476,4 +510,4 @@ static struct platform_driver spacemit_i2s_driver = { module_platform_driver(spacemit_i2s_driver); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("I2S bus driver for SpacemiT K1 SoC"); +MODULE_DESCRIPTION("I2S bus driver for SpacemiT K1/K3 SoC"); From 9d111887d5e43bbeffc8a972146a2698c1ff3a6e Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 22 May 2026 21:33:57 +0800 Subject: [PATCH 043/521] UPSTREAM: ASoC: soc-dai: add shared BCLK clock for cross-DAI rate constraints Add a bclk field to struct snd_soc_dai and a helper function snd_soc_dai_set_bclk_clk() that platform drivers can use to declare which clock is their BCLK. Also cache the bclk_ratio in snd_soc_dai_set_bclk_ratio() so that the framework can use it later in hw_rule evaluation for TDM configurations where BCLK = rate * slots * slot_width. When multiple DAIs on the same card share the same physical BCLK (detected via clk_is_match()), the ASoC core can automatically constrain their hw_params so that the resulting BCLK rates are compatible. This commit adds the data structure support; the actual constraint logic follows in the next patch. Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260522-i2s-same-blk-v4-1-a71a86faaa20@linux.spacemit.com Signed-off-by: Mark Brown (cherry picked from commit 94bdfad3a665da68731deb56b0fe80f00a3e6da8) Signed-off-by: Han Gao --- include/sound/soc-dai.h | 7 +++++++ sound/soc/soc-dai.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 6a42812bba8cad..df010a91b35059 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -17,6 +17,7 @@ struct snd_pcm_substream; struct snd_soc_dapm_widget; struct snd_compr_stream; +struct clk; /* * DAI hardware audio formats. @@ -188,6 +189,8 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); +void snd_soc_dai_set_bclk_clk(struct snd_soc_dai *dai, struct clk *bclk); + /* Digital Audio interface formatting */ int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd); u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority); @@ -473,6 +476,10 @@ struct snd_soc_dai { unsigned int symmetric_channels; unsigned int symmetric_sample_bits; + /* shared BCLK clock for cross-DAI rate constraints */ + struct clk *bclk; + unsigned int bclk_ratio; /* BCLK = rate * bclk_ratio (0 = use channels * sample_bits) */ + /* parent platform/codec */ struct snd_soc_component *component; diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 2f370fda12665d..1719ddcefa4b03 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -116,10 +116,28 @@ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) dai->driver->ops->set_bclk_ratio) ret = dai->driver->ops->set_bclk_ratio(dai, ratio); + if (!ret) + dai->bclk_ratio = ratio; + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); +/** + * snd_soc_dai_set_bclk_clk - set the BCLK clock for shared clock detection + * @dai: DAI + * @bclk: BCLK clock pointer (or NULL to clear) + * + * When multiple DAIs share the same physical BCLK (detected via + * clk_is_match()), the ASoC core will automatically constrain their + * hw_params so that the resulting BCLK rates are compatible. + */ +void snd_soc_dai_set_bclk_clk(struct snd_soc_dai *dai, struct clk *bclk) +{ + dai->bclk = bclk; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_clk); + int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai; From ad0ec0b373319ded7f1f595d36ad910779f7d3a5 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 22 May 2026 21:33:58 +0800 Subject: [PATCH 044/521] UPSTREAM: ASoC: soc-pcm: add DEFINE_GUARD for snd_soc_card_mutex Define a guard class wrapping snd_soc_card_mutex_lock() and snd_soc_card_mutex_unlock() so that scope-based locking can be used while still picking up the SND_SOC_CARD_CLASS_RUNTIME lockdep subclass. Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260522-i2s-same-blk-v4-2-a71a86faaa20@linux.spacemit.com Signed-off-by: Mark Brown (cherry picked from commit 2555c62275a10bb7dfb4476ebe73d48df11f3112) Signed-off-by: Han Gao --- sound/soc/soc-pcm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 9b12eedb77c331..25e494c4ed8129 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -26,6 +26,9 @@ #include #include + +DEFINE_GUARD(snd_soc_card_mutex, struct snd_soc_card *, + snd_soc_card_mutex_lock(_T), snd_soc_card_mutex_unlock(_T)) #define soc_pcm_ret(rtd, ret) _soc_pcm_ret(rtd, __func__, ret) static inline int _soc_pcm_ret(struct snd_soc_pcm_runtime *rtd, const char *func, int ret) From 94d6dcb877ca8252e034ed3ea3f6cfc0b45ba145 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 22 May 2026 21:33:59 +0800 Subject: [PATCH 045/521] UPSTREAM: ASoC: soc-pcm: constrain hw_params when DAIs share the same BCLK When multiple CPU DAIs on the same sound card share the same physical BCLK, add a hw_rule during PCM open that constrains the sample rate so the resulting BCLK rate stays consistent across all sharing DAIs. The rule callback scans all DAIs on the card at hw_refine time, looking for an active peer that shares the same physical BCLK (via clk_is_match()) and has already completed hw_params (checked via dai->symmetric_rate != 0). This ensures the constraint uses the real BCLK rate established by the peer's clk_set_rate() in hw_params, not a stale boot-time default. The first DAI to complete hw_params is unconstrained (no active peer yet); subsequent DAIs are constrained to match. The rule supports two modes: - If the DAI has an explicit bclk_ratio set (e.g. for TDM where BCLK = rate * slots * slot_width), the rate is constrained to active_bclk_rate / bclk_ratio. - Otherwise, the default formula BCLK = rate * channels * sample_bits is used to derive the valid rate range. The constraint is purely additive: DAIs that do not set a bclk clock pointer are completely unaffected. Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260522-i2s-same-blk-v4-3-a71a86faaa20@linux.spacemit.com Signed-off-by: Mark Brown (cherry picked from commit c8c2ffd722a6e497b9c6bb9961a7afb6ee5e2c28) Signed-off-by: Han Gao --- sound/soc/soc-pcm.c | 116 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 25e494c4ed8129..0e49290a8c903b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -470,6 +471,114 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, return 0; } +/* + * Shared BCLK constraint: when multiple DAIs share the same physical BCLK, + * constrain hw_params so that the BCLK rate (rate * channels * sample_bits, + * or rate * slots * slot_width for TDM) remains consistent. + */ + +static int soc_pcm_shared_bclk_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct snd_soc_card *card = dai->component->card; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *other_dai; + unsigned long active_bclk_rate = 0; + struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval constraint = { .empty = 1 }; + unsigned int target_rate; + int i; + + /* Protect the rtd list traversal with the ASoC card mutex helper. */ + guard(snd_soc_card_mutex)(card); + + /* Scan all DAIs on the card for an active peer sharing the same BCLK */ + for_each_card_rtds(card, rtd) { + for_each_rtd_cpu_dais(rtd, i, other_dai) { + if (other_dai == dai) + continue; + if (!other_dai->bclk) + continue; + if (!snd_soc_dai_active(other_dai)) + continue; + /* + * Skip peers whose hw_params hasn't run yet. + * symmetric_rate is set by soc_pcm_set_dai_params() + * after snd_soc_dai_hw_params(), so non-zero means + * the DAI's clk_set_rate() has already executed. + */ + if (!other_dai->symmetric_rate) + continue; + if (!clk_is_match(dai->bclk, other_dai->bclk)) + continue; + + active_bclk_rate = clk_get_rate(other_dai->bclk); + if (active_bclk_rate) + goto found; + } + } + + return 0; + +found: + if (dai->bclk_ratio) { + /* + * Driver has set an explicit BCLK ratio (e.g. for TDM where + * BCLK = rate * slots * slot_width). The only valid rate is + * active_bclk_rate / bclk_ratio. + */ + target_rate = active_bclk_rate / dai->bclk_ratio; + + constraint.min = target_rate; + constraint.max = target_rate; + } else { + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *sample_bits = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + + /* + * Default: BCLK = rate * channels * sample_bits. + * Calculate the range of valid rates given the current + * channel and sample_bits intervals. + */ + if (!channels->min || !sample_bits->min) + return 0; + + constraint.max = active_bclk_rate / + ((unsigned long)channels->min * sample_bits->min); + + if (channels->max && sample_bits->max) + constraint.min = active_bclk_rate / + ((unsigned long)channels->max * sample_bits->max); + else + constraint.min = constraint.max; + } + + constraint.integer = 1; + constraint.empty = 0; + + return snd_interval_refine(rate, &constraint); +} + +static int soc_pcm_apply_shared_bclk(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!dai->bclk) + return 0; + + dev_dbg(dai->dev, + "ASoC: registering shared BCLK rate constraint\n"); + + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + soc_pcm_shared_bclk_rule_rate, dai, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + -1); +} + static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -906,6 +1015,13 @@ static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd, if (ret != 0) goto err; } + + /* Shared BCLK constraint across DAIs on the same card */ + for_each_rtd_cpu_dais(rtd, i, dai) { + ret = soc_pcm_apply_shared_bclk(substream, dai); + if (ret != 0) + goto err; + } dynamic: snd_soc_runtime_activate(rtd, substream->stream); ret = 0; From 19ea2729119fcca3d7c82fb2a7d114df0e17f1be Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:05 +0800 Subject: [PATCH 046/521] UPSTREAM: dt-bindings: gpio: dwapb: allow GPIO hogs GPIO hogs are described in the gpio.txt binding as automatic default GPIO configuration items. Allow them for GPIO ports in DesignWare APB GPIO controller nodes. Cc: Hoan Tran Cc: Linus Walleij Cc: Bartosz Golaszewski Cc: Serge Semin Signed-off-by: Icenowy Zheng Acked-by: Conor Dooley Link: https://patch.msgid.link/20260507081710.4090814-8-zhengxingda@iscas.ac.cn Signed-off-by: Bartosz Golaszewski (cherry picked from commit f76c8be440e53465a306c95a7d50ca8675252f82) Signed-off-by: Han Gao --- .../devicetree/bindings/gpio/snps,dw-apb-gpio.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml index bba6f5b6606fdf..55069533f6d912 100644 --- a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml @@ -95,6 +95,12 @@ patternProperties: '#interrupt-cells': const: 2 + patternProperties: + "^.+-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + required: - compatible - reg From 39447b64b7ecce6caffd1da6d7bf474286fc1a8e Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Wed, 13 May 2026 09:19:49 +0200 Subject: [PATCH 047/521] UPSTREAM: riscv: dts: spacemit: k1-musepi-pro: add PMIC and power infrastructure Enable i2c8 and add the connected SpacemiT P1 PMIC with its related regulators for the board's power infrastructure and voltage regulation support. Signed-off-by: Andre Heider Link: https://patch.msgid.link/20260513071958.29574-2-a.heider@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit e2dac7c7a3a8970feeeb758f782ce86b89625ebd) Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k1-musepi-pro.dts | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index 29e333b670cf0a..653e5a1bf41bad 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -17,6 +17,7 @@ aliases { ethernet0 = ð0; serial0 = &uart0; + i2c8 = &i2c8; }; chosen { @@ -33,6 +34,15 @@ default-state = "on"; }; }; + + reg_vcc_4v: regulator-vcc-4v { + compatible = "regulator-fixed"; + regulator-name = "VCC4V0"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <4000000>; + regulator-boot-on; + regulator-always-on; + }; }; &emmc { @@ -72,6 +82,130 @@ status = "okay"; }; +&i2c8 { + pinctrl-0 = <&i2c8_cfg>; + pinctrl-names = "default"; + status = "okay"; + + pmic@41 { + compatible = "spacemit,p1"; + reg = <0x41>; + interrupts = <64>; + vin1-supply = <®_vcc_4v>; + vin2-supply = <®_vcc_4v>; + vin3-supply = <®_vcc_4v>; + vin4-supply = <®_vcc_4v>; + vin5-supply = <®_vcc_4v>; + vin6-supply = <®_vcc_4v>; + aldoin-supply = <®_vcc_4v>; + dldoin1-supply = <&buck5>; + dldoin2-supply = <&buck5>; + + regulators { + buck1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck3_1v8: buck3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck4_3v3: buck4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck5: buck5 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + aldo1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + }; + + aldo2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + aldo3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + aldo4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + }; + + dldo2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-always-on; + }; + + dldo5 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-always-on; + }; + + dldo7 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + }; + }; +}; + &uart0 { pinctrl-0 = <&uart0_2_cfg>; pinctrl-names = "default"; From 09499d79506edb78a95eaca91404cfa4334a918a Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Wed, 13 May 2026 09:19:50 +0200 Subject: [PATCH 048/521] UPSTREAM: riscv: dts: spacemit: k1-musepi-pro: add 24c02 eeprom Enable i2c2 and add the connected GT24C02B EEPROM. It contains an ONIE TLV table: => tlv_eeprom TLV: 0 [ 12.162] TlvInfo Header: [ 12.162] Id String: TlvInfo [ 12.165] Version: 1 [ 12.168] Total Length: 58 [ 12.171] TLV Name Code Len Value [ 12.175] -------------------- ---- --- ----- [ 12.179] Product Name 0x21 16 k1-x_MUSE-Pi-Pro [ 12.184] Serial Number 0x23 17 BPMIMXXXXXXXXXXXX [ 12.189] Unknown 0x41 1 0x02 [ 12.194] Base MAC Address 0x24 6 FE:FE:FE:XX:XX:XX [ 12.199] MAC Addresses 0x2A 2 2 [ 12.203] CRC-32 0xFE 4 0x395ECD34 [ 12.207] Checksum is valid. (With 0x41 as TLV_CODE_DDR_CSNUM) Signed-off-by: Andre Heider Link: https://patch.msgid.link/20260513071958.29574-3-a.heider@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit 6bc75cfa56e885c2eab80e215f0a610075505846) Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k1-musepi-pro.dts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index 653e5a1bf41bad..b8e73eed8e62bf 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -17,6 +17,7 @@ aliases { ethernet0 = ð0; serial0 = &uart0; + i2c2 = &i2c2; i2c8 = &i2c8; }; @@ -82,6 +83,38 @@ status = "okay"; }; +&i2c2 { + pinctrl-0 = <&i2c2_0_cfg>; + pinctrl-names = "default"; + status = "okay"; + + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + vcc-supply = <&buck3_1v8>; /* EEPROM_VCC1V8 */ + pagesize = <8>; + read-only; + size = <256>; + + nvmem-layout { + compatible = "onie,tlv-layout"; + + product-name { + }; + + serial-number { + }; + + mac-address { + #nvmem-cell-cells = <1>; + }; + + num-macs { + }; + }; + }; +}; + &i2c8 { pinctrl-0 = <&i2c8_cfg>; pinctrl-names = "default"; From 320006911c46d287b202145a310c6b8e563ce4d2 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Wed, 13 May 2026 09:19:51 +0200 Subject: [PATCH 049/521] UPSTREAM: riscv: dts: spacemit: k1-musepi-pro: enable QSPI and add SPI NOR Add the QSPI controller node and describe the attached SPI NOR flash (Winbond W25Q64FWSSAQ). Add a corresponding vendor flash partition layout. Signed-off-by: Andre Heider Link: https://patch.msgid.link/20260513071958.29574-4-a.heider@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit 735b1b205d07bf7bfa55b1f50f5485b9cc42a251) Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k1-musepi-pro.dts | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index b8e73eed8e62bf..74841cffd5f572 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -44,6 +44,15 @@ regulator-boot-on; regulator-always-on; }; + + reg_qspi_vcc1v833: regulator-qspi-vcc1v833 { + compatible = "regulator-fixed"; + regulator-name = "QSPI_VCC1833"; + regulator-min-microvolt = <1833000>; + regulator-max-microvolt = <1833000>; + regulator-always-on; + vin-supply = <&buck4_3v3>; + }; }; &emmc { @@ -239,6 +248,47 @@ }; }; +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + vcc-supply = <®_qspi_vcc1v833>; + m25p,fast-read; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootinfo@0 { + reg = <0x0 0x10000>; + }; + private@10000 { + reg = <0x10000 0x10000>; + }; + fsbl@20000 { + reg = <0x20000 0x40000>; + }; + env@60000 { + reg = <0x60000 0x10000>; + }; + opensbi@70000 { + reg = <0x70000 0x30000>; + }; + uboot@a0000 { + reg = <0xa0000 0x760000>; + }; + }; + }; +}; + &uart0 { pinctrl-0 = <&uart0_2_cfg>; pinctrl-names = "default"; From 361bd5b13a40b428e6b582cb0b2554ff4a0978ab Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Wed, 13 May 2026 09:19:52 +0200 Subject: [PATCH 050/521] UPSTREAM: riscv: dts: spacemit: k1-musepi-pro: enable USB 3 ports Enable the DWC3 USB 3.0 controller, its associated combo_phy (USB 3 PHY) and usbphy2 (USB 2 PHY) on the MusePi Pro board. The board uses a VLI VL817 hub, providing four ports. Signed-off-by: Andre Heider Link: https://patch.msgid.link/20260513071958.29574-5-a.heider@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit 83c658c7a82304a2bc944cdd4a5b4f98d19322f0) Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k1-musepi-pro.dts | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index 74841cffd5f572..b284bf41688036 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -53,6 +53,26 @@ regulator-always-on; vin-supply = <&buck4_3v3>; }; + + reg_5v_vbus: regulator-5v-vbus { + compatible = "regulator-fixed"; + regulator-name = "5V_VBUS"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + gpio = <&gpio K1_GPIO(79) GPIO_ACTIVE_HIGH>; /* USB3_PWREN */ + enable-active-high; + }; + + reg_vcc5v_hub: regulator-vcc5v-hub { + compatible = "regulator-fixed"; + regulator-name = "VCC5V0_HUB"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + gpio = <&gpio K1_GPIO(127) GPIO_ACTIVE_HIGH>; /* HUB_PWREN */ + enable-active-high; + }; }; &emmc { @@ -65,6 +85,10 @@ status = "okay"; }; +&combo_phy { + status = "okay"; +}; + ð0 { phy-handle = <&rgmii0>; phy-mode = "rgmii-id"; @@ -294,3 +318,31 @@ pinctrl-names = "default"; status = "okay"; }; + +&usbphy2 { + status = "okay"; +}; + +&usb_dwc3 { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + vbus-supply = <®_5v_vbus>; + status = "okay"; + + hub_2_0: hub@1 { + compatible = "usb2109,2817"; + reg = <0x1>; + vdd-supply = <®_vcc5v_hub>; + peer-hub = <&hub_3_0>; + reset-gpios = <&gpio K1_GPIO(123) GPIO_ACTIVE_LOW>; /* HUB_RST */ + }; + + hub_3_0: hub@2 { + compatible = "usb2109,817"; + reg = <0x2>; + vdd-supply = <®_vcc5v_hub>; + peer-hub = <&hub_2_0>; + reset-gpios = <&gpio K1_GPIO(123) GPIO_ACTIVE_LOW>; /* HUB_RST */ + }; +}; From f306ac6d4fcac676864cb609222ef877bd236671 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Wed, 13 May 2026 09:19:53 +0200 Subject: [PATCH 051/521] UPSTREAM: riscv: dts: spacemit: k1-musepi-pro: enable PCIe ports Enable the two PCIe controllers along with their associated PHYs. They are routed to the M.2 M-key connector and to the PCIe slot. Signed-off-by: Andre Heider Link: https://patch.msgid.link/20260513071958.29574-6-a.heider@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit d9ab0a855ba13f1db3ae16539348714c74010a22) Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k1-musepi-pro.dts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index b284bf41688036..898b537ba0bcab 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -36,6 +36,14 @@ }; }; + reg_pcie_vcc_3v3: regulator-pcie-vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "PCIE_VCC3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + reg_vcc_4v: regulator-vcc-4v { compatible = "regulator-fixed"; regulator-name = "VCC4V0"; @@ -272,6 +280,36 @@ }; }; +&pcie1_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_3_cfg>; + status = "okay"; +}; + +&pcie1_port { + phys = <&pcie1_phy>; + vpcie3v3-supply = <®_pcie_vcc_3v3>; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_4_cfg>; + status = "okay"; +}; + +&pcie2_port { + phys = <&pcie2_phy>; + vpcie3v3-supply = <®_pcie_vcc_3v3>; +}; + +&pcie2 { + status = "okay"; +}; + &qspi { pinctrl-names = "default"; pinctrl-0 = <&qspi_cfg>; From c03e6971f5c0d571ece668c9285b3bf6c139d65a Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Wed, 13 May 2026 09:19:54 +0200 Subject: [PATCH 052/521] UPSTREAM: riscv: dts: spacemit: k1-musepi-pro: set default console baud rate Allow serial output with the same uboot/opensbi settings so the console works without providing a cmdline. Signed-off-by: Andre Heider Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260513071958.29574-7-a.heider@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit f125c6085fd775f09a316f61cc89a0ae86826895) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index 898b537ba0bcab..4aef287353cab6 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -22,7 +22,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; leds { From 3007c11e2a3ffc3ebd3aad7546e3dae9c133a0b1 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 21 May 2026 00:24:41 +0000 Subject: [PATCH 053/521] UPSTREAM: riscv: dts: spacemit: k3: Add pwm support Populate all pwm device tree nodes for SpacemiT K3 SoC, also documents the pinctrl info which would easily help to enable them in future. Link: https://patch.msgid.link/20260521-04-k3-pwm-dts-v4-1-04d4de0f2fc8@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit 844c29197366e25ad361cf725d80acb4f10e3e19) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 590 +++++++++++++++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 220 +++++++ 2 files changed, 810 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi index 23899d3f308a02..252c64af76fe19 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -56,6 +56,596 @@ }; }; + /omit-if-no-ref/ + pwm0_0_cfg: pwm0-0-cfg { + pwm0-0-pins { + pinmux = ; /* pwm0 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm0_1_cfg: pwm0-1-cfg { + pwm0-1-pins { + pinmux = ; /* pwm0 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm1_0_cfg: pwm1-0-cfg { + pwm1-0-pins { + pinmux = ; /* pwm1 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm1_1_cfg: pwm1-1-cfg { + pwm1-1-pins { + pinmux = ; /* pwm1 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm1_2_cfg: pwm1-2-cfg { + pwm1-2-pins { + pinmux = ; /* pwm1 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm2_0_cfg: pwm2-0-cfg { + pwm2-0-pins { + pinmux = ; /* pwm2 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm2_1_cfg: pwm2-1-cfg { + pwm2-1-pins { + pinmux = ; /* pwm2 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm2_2_cfg: pwm2-2-cfg { + pwm2-2-pins { + pinmux = ; /* pwm2 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm2_3_cfg: pwm2-3-cfg { + pwm2-3-pins { + pinmux = ; /* pwm2 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm3_0_cfg: pwm3-0-cfg { + pwm3-0-pins { + pinmux = ; /* pwm3 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm3_1_cfg: pwm3-1-cfg { + pwm3-1-pins { + pinmux = ; /* pwm3 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm3_2_cfg: pwm3-2-cfg { + pwm3-2-pins { + pinmux = ; /* pwm3 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm3_3_cfg: pwm3-3-cfg { + pwm3-3-pins { + pinmux = ; /* pwm3 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm4_0_cfg: pwm4-0-cfg { + pwm4-0-pins { + pinmux = ; /* pwm4 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm4_1_cfg: pwm4-1-cfg { + pwm4-1-pins { + pinmux = ; /* pwm4 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm4_2_cfg: pwm4-2-cfg { + pwm4-2-pins { + pinmux = ; /* pwm4 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm5_0_cfg: pwm5-0-cfg { + pwm5-0-pins { + pinmux = ; /* pwm5 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm5_1_cfg: pwm5-1-cfg { + pwm5-1-pins { + pinmux = ; /* pwm5 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm5_2_cfg: pwm5-2-cfg { + pwm5-2-pins { + pinmux = ; /* pwm5 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm6_0_cfg: pwm6-0-cfg { + pwm6-0-pins { + pinmux = ; /* pwm6 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm6_1_cfg: pwm6-1-cfg { + pwm6-1-pins { + pinmux = ; /* pwm6 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm6_2_cfg: pwm6-2-cfg { + pwm6-2-pins { + pinmux = ; /* pwm6 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm7_0_cfg: pwm7-0-cfg { + pwm7-0-pins { + pinmux = ; /* pwm7 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm7_1_cfg: pwm7-1-cfg { + pwm7-1-pins { + pinmux = ; /* pwm7 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm7_2_cfg: pwm7-2-cfg { + pwm7-2-pins { + pinmux = ; /* pwm7 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm8_0_cfg: pwm8-0-cfg { + pwm8-0-pins { + pinmux = ; /* pwm8 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm8_1_cfg: pwm8-1-cfg { + pwm8-1-pins { + pinmux = ; /* pwm8 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm8_2_cfg: pwm8-2-cfg { + pwm8-2-pins { + pinmux = ; /* pwm8 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm9_0_cfg: pwm9-0-cfg { + pwm9-0-pins { + pinmux = ; /* pwm9 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm9_1_cfg: pwm9-1-cfg { + pwm9-1-pins { + pinmux = ; /* pwm9 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm9_2_cfg: pwm9-2-cfg { + pwm9-2-pins { + pinmux = ; /* pwm9 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm10_0_cfg: pwm10-0-cfg { + pwm10-0-pins { + pinmux = ; /* pwm10 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm10_1_cfg: pwm10-1-cfg { + pwm10-1-pins { + pinmux = ; /* pwm10 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm10_2_cfg: pwm10-2-cfg { + pwm10-2-pins { + pinmux = ; /* pwm10 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm11_0_cfg: pwm11-0-cfg { + pwm11-0-pins { + pinmux = ; /* pwm11 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm11_1_cfg: pwm11-1-cfg { + pwm11-1-pins { + pinmux = ; /* pwm11 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm12_0_cfg: pwm12-0-cfg { + pwm12-0-pins { + pinmux = ; /* pwm12 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm12_1_cfg: pwm12-1-cfg { + pwm12-1-pins { + pinmux = ; /* pwm12 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm13_0_cfg: pwm13-0-cfg { + pwm13-0-pins { + pinmux = ; /* pwm13 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm13_1_cfg: pwm13-1-cfg { + pwm13-1-pins { + pinmux = ; /* pwm13 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm13_2_cfg: pwm13-2-cfg { + pwm13-2-pins { + pinmux = ; /* pwm13 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm14_0_cfg: pwm14-0-cfg { + pwm14-0-pins { + pinmux = ; /* pwm14 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm14_1_cfg: pwm14-1-cfg { + pwm14-1-pins { + pinmux = ; /* pwm14 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm14_2_cfg: pwm14-2-cfg { + pwm14-2-pins { + pinmux = ; /* pwm14 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm15_0_cfg: pwm15-0-cfg { + pwm15-0-pins { + pinmux = ; /* pwm15 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm15_1_cfg: pwm15-1-cfg { + pwm15-1-pins { + pinmux = ; /* pwm15 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm15_2_cfg: pwm15-2-cfg { + pwm15-2-pins { + pinmux = ; /* pwm15 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm16_0_cfg: pwm16-0-cfg { + pwm16-0-pins { + pinmux = ; /* pwm16 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm16_1_cfg: pwm16-1-cfg { + pwm16-1-pins { + pinmux = ; /* pwm16 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm16_2_cfg: pwm16-2-cfg { + pwm16-2-pins { + pinmux = ; /* pwm16 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm17_0_cfg: pwm17-0-cfg { + pwm17-0-pins { + pinmux = ; /* pwm17 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm17_1_cfg: pwm17-1-cfg { + pwm17-1-pins { + pinmux = ; /* pwm17 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm17_2_cfg: pwm17-2-cfg { + pwm17-2-pins { + pinmux = ; /* pwm17 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm18_0_cfg: pwm18-0-cfg { + pwm18-0-pins { + pinmux = ; /* pwm18 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm18_1_cfg: pwm18-1-cfg { + pwm18-1-pins { + pinmux = ; /* pwm18 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm18_2_cfg: pwm18-2-cfg { + pwm18-2-pins { + pinmux = ; /* pwm18 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm19_0_cfg: pwm19-0-cfg { + pwm19-0-pins { + pinmux = ; /* pwm19 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm19_1_cfg: pwm19-1-cfg { + pwm19-1-pins { + pinmux = ; /* pwm19 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + pwm19_2_cfg: pwm19-2-cfg { + pwm19-2-pins { + pinmux = ; /* pwm19 */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + /omit-if-no-ref/ uart0_0_cfg: uart0-0-cfg { uart0-0-pins { diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 9e3c005f7bdf9c..97cc9e2dd55467 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -808,6 +808,226 @@ status = "disabled"; }; + pwm0: pwm@d401a000 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401a000 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM0>, + <&syscon_apbc CLK_APBC_PWM0_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM0>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm1: pwm@d401a400 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401a400 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM1>, + <&syscon_apbc CLK_APBC_PWM1_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM1>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm2: pwm@d401a800 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401a800 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM2>, + <&syscon_apbc CLK_APBC_PWM2_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM2>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm3: pwm@d401ac00 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401ac00 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM3>, + <&syscon_apbc CLK_APBC_PWM3_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM3>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm4: pwm@d401b000 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401b000 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM4>, + <&syscon_apbc CLK_APBC_PWM4_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM4>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm5: pwm@d401b400 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401b400 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM5>, + <&syscon_apbc CLK_APBC_PWM5_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM5>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm6: pwm@d401b800 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401b800 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM6>, + <&syscon_apbc CLK_APBC_PWM6_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM6>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm7: pwm@d401bc00 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd401bc00 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM7>, + <&syscon_apbc CLK_APBC_PWM7_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM7>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm8: pwm@d4020000 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020000 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM8>, + <&syscon_apbc CLK_APBC_PWM8_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM8>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm9: pwm@d4020400 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020400 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM9>, + <&syscon_apbc CLK_APBC_PWM9_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM9>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm10: pwm@d4020800 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020800 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM10>, + <&syscon_apbc CLK_APBC_PWM10_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM10>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm11: pwm@d4020c00 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4020c00 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM11>, + <&syscon_apbc CLK_APBC_PWM11_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM11>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm12: pwm@d4021000 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021000 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM12>, + <&syscon_apbc CLK_APBC_PWM12_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM12>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm13: pwm@d4021400 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021400 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM13>, + <&syscon_apbc CLK_APBC_PWM13_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM13>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm14: pwm@d4021800 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021800 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM14>, + <&syscon_apbc CLK_APBC_PWM14_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM14>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm15: pwm@d4021c00 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4021c00 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM15>, + <&syscon_apbc CLK_APBC_PWM15_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM15>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm16: pwm@d4022000 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022000 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM16>, + <&syscon_apbc CLK_APBC_PWM16_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM16>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm17: pwm@d4022400 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022400 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM17>, + <&syscon_apbc CLK_APBC_PWM17_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM17>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm18: pwm@d4022800 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022800 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM18>, + <&syscon_apbc CLK_APBC_PWM18_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM18>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm19: pwm@d4022c00 { + compatible = "spacemit,k3-pwm", "marvell,pxa910-pwm"; + reg = <0x0 0xd4022c00 0x0 0x10>; + clocks = <&syscon_apbc CLK_APBC_PWM19>, + <&syscon_apbc CLK_APBC_PWM19_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_PWM19>; + #pwm-cells = <3>; + status = "disabled"; + }; + pinctrl: pinctrl@d401e000 { compatible = "spacemit,k3-pinctrl"; reg = <0x0 0xd401e000 0x0 0x1000>; From 82c1354f469fd230a1ea853557adced02798ded2 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 054/521] UPSTREAM: riscv: use sysfs_emit in cpu_show_ghostwrite Replace sprintf() with sysfs_emit() in cpu_show_ghostwrite(), which is preferred for formatting sysfs output because it provides safer bounds checking. While the current code only emits fixed strings that fit easily within PAGE_SIZE, use sysfs_emit() to follow secure coding best practices. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260510165420.109453-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley (cherry picked from commit 7d0dec57e52c7b5e14ba4ab6f86e9c17b850f1ec) Signed-off-by: Han Gao --- arch/riscv/kernel/bugs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/bugs.c b/arch/riscv/kernel/bugs.c index 3655fe7d678cd1..e5758e3f1c7ef4 100644 --- a/arch/riscv/kernel/bugs.c +++ b/arch/riscv/kernel/bugs.c @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -46,15 +46,15 @@ ssize_t cpu_show_ghostwrite(struct device *dev, struct device_attribute *attr, c if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR)) { switch (ghostwrite_state) { case UNAFFECTED: - return sprintf(buf, "Not affected\n"); + return sysfs_emit(buf, "Not affected\n"); case MITIGATED: - return sprintf(buf, "Mitigation: xtheadvector disabled\n"); + return sysfs_emit(buf, "Mitigation: xtheadvector disabled\n"); case VULNERABLE: fallthrough; default: - return sprintf(buf, "Vulnerable\n"); + return sysfs_emit(buf, "Vulnerable\n"); } - } else { - return sprintf(buf, "Not affected\n"); } + + return sysfs_emit(buf, "Not affected\n"); } From 232ba28d50f5b22cc093b4ae9cb06ba678c1943e Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:09 +0000 Subject: [PATCH 055/521] UPSTREAM: clk: spacemit: k3: Switch to pll2_d6 as parent for PCIe clock According to SpacemiT updated docs, the PCIe master and slave clock's parent is the pll2_d6 clock, so fix it. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-1-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit d8a4cef90b1a4ae9196a5bfba683eb9a0c75acdc) Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index bb8b75bdbdb30d..1a53b14739fed0 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -947,16 +947,16 @@ static const struct clk_parent_data edp1_pclk_parents[] = { }; CCU_MUX_GATE_DEFINE(edp1_pxclk, edp1_pclk_parents, APMU_LCD_EDP_CTRL, 18, 1, BIT(17), 0); -CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); -CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); -CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); -CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); -CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); -CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); -CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); -CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); -CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); -CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); +CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); +CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); +CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); +CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); +CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); +CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); +CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); +CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); +CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); +CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); static const struct clk_parent_data emac_1588_parents[] = { CCU_PARENT_NAME(vctcxo_24m), From 608f0e2500dcf4dad5b457b6db5cf6dfc80979c2 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:10 +0000 Subject: [PATCH 056/521] UPSTREAM: clk: spacemit: k3: Fix PCIe clock register offset The offset of PCIe Clock CTRL register for port B and C controller was wrongly swapped, correct it here. Fixes: 091d19cc2401 ("clk: spacemit: k3: extract common header") Acked-by: Conor Dooley Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-2-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit 2f20c859a82a291483a8b3f01cbfbb1642782a14) Signed-off-by: Han Gao --- include/soc/spacemit/k3-syscon.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/soc/spacemit/k3-syscon.h b/include/soc/spacemit/k3-syscon.h index 0299bea065a05d..a68255dd641f31 100644 --- a/include/soc/spacemit/k3-syscon.h +++ b/include/soc/spacemit/k3-syscon.h @@ -168,8 +168,8 @@ #define APMU_CPU_C2_CLK_CTRL 0x394 #define APMU_CPU_C3_CLK_CTRL 0x208 #define APMU_PCIE_CLK_RES_CTRL_A 0x1f0 -#define APMU_PCIE_CLK_RES_CTRL_B 0x1c8 -#define APMU_PCIE_CLK_RES_CTRL_C 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_B 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_C 0x1c8 #define APMU_PCIE_CLK_RES_CTRL_D 0x1e0 #define APMU_PCIE_CLK_RES_CTRL_E 0x1e8 #define APMU_EMAC0_CLK_RES_CTRL 0x3e4 From 4bb98263999eeabf266c7fe8270561a6c8ecc80d Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:11 +0000 Subject: [PATCH 057/521] UPSTREAM: dt-bindings: soc: spacemit: k3: Add PCIe DBI clock IDs Add clock IDs of PCIe DBI (Data Bus Interface) clock. Acked-by: Conor Dooley Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-3-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit 7a2db70a3196717bfb01415b6dde1f90d4291ab1) Signed-off-by: Han Gao --- include/dt-bindings/clock/spacemit,k3-clocks.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/dt-bindings/clock/spacemit,k3-clocks.h b/include/dt-bindings/clock/spacemit,k3-clocks.h index b22336f3ae4078..dfae52547cda49 100644 --- a/include/dt-bindings/clock/spacemit,k3-clocks.h +++ b/include/dt-bindings/clock/spacemit,k3-clocks.h @@ -380,6 +380,11 @@ #define CLK_APMU_ISIM_VCLK1 86 #define CLK_APMU_ISIM_VCLK2 87 #define CLK_APMU_ISIM_VCLK3 88 +#define CLK_APMU_PCIE_PORTA_DBI 89 +#define CLK_APMU_PCIE_PORTB_DBI 90 +#define CLK_APMU_PCIE_PORTC_DBI 91 +#define CLK_APMU_PCIE_PORTD_DBI 92 +#define CLK_APMU_PCIE_PORTE_DBI 93 /* DCIU clocks */ #define CLK_DCIU_HDMA 0 From cc142a4d0a27501c72d47b932799de867592a368 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:12 +0000 Subject: [PATCH 058/521] UPSTREAM: clk: spacemit: k3: Add PCIe DBI clock Add PCIe DBI (Data Bus Interface) clock which was missing, This will support PCIe driver to explicitly request and enable all clocks that needed. Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-4-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit a37c75d7b5bb7f3344b0e639b313ac5ace6244ff) Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 1a53b14739fed0..cb0c4277f72a8e 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -949,14 +949,19 @@ CCU_MUX_GATE_DEFINE(edp1_pxclk, edp1_pclk_parents, APMU_LCD_EDP_CTRL, 18, 1, BIT CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); +CCU_GATE_DEFINE(pciea_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(0), 0); CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); +CCU_GATE_DEFINE(pcieb_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(0), 0); CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); +CCU_GATE_DEFINE(pciec_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(0), 0); CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); +CCU_GATE_DEFINE(pcied_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(0), 0); CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); +CCU_GATE_DEFINE(pciee_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(0), 0); static const struct clk_parent_data emac_1588_parents[] = { CCU_PARENT_NAME(vctcxo_24m), @@ -1391,14 +1396,19 @@ static struct clk_hw *k3_ccu_apmu_hws[] = { [CLK_APMU_EDP1_PXCLK] = &edp1_pxclk.common.hw, [CLK_APMU_PCIE_PORTA_MSTE] = &pciea_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTA_SLV] = &pciea_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTA_DBI] = &pciea_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTB_MSTE] = &pcieb_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTB_SLV] = &pcieb_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTB_DBI] = &pcieb_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTC_MSTE] = &pciec_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTC_SLV] = &pciec_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTC_DBI] = &pciec_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTD_MSTE] = &pcied_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTD_SLV] = &pcied_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTD_DBI] = &pcied_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTE_MSTE] = &pciee_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTE_SLV] = &pciee_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTE_DBI] = &pciee_dbi_clk.common.hw, [CLK_APMU_EMAC0_BUS] = &emac0_bus_clk.common.hw, [CLK_APMU_EMAC0_REF] = &emac0_ref_clk.common.hw, [CLK_APMU_EMAC0_1588] = &emac0_1588_clk.common.hw, From 6fe0535e51fbc645bc404afd99b5c9b67ebe7043 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Sat, 9 May 2026 18:00:00 +0800 Subject: [PATCH 059/521] UPSTREAM: riscv: dts: spacemit: enable eMMC for OrangePi RV2 The OrangePi RV2 board has one eMMC slot, so enable eMMC. Tested using a 16 GiB AJTD4R eMMC module. Signed-off-by: Chukun Pan Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260509100000.3315109-1-amadeus@jmu.edu.cn Signed-off-by: Yixun Lan (cherry picked from commit f068b204555ad62d6a841a49feb4ea8c4f45b25c) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index 3a829e3c9cbcd4..c95ca38e3d4a1e 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -78,6 +78,16 @@ status = "okay"; }; +&emmc { + bus-width = <8>; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + no-sd; + no-sdio; + non-removable; + status = "okay"; +}; + ð0 { phy-handle = <&rgmii0>; phy-mode = "rgmii-id"; From 40fff4ea258af984bb3e476296ad16d99b226f73 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:56 +0200 Subject: [PATCH 060/521] UPSTREAM: dt-bindings: mmc: spacemit,sdhci: add pinctrl support for voltage switching Document pinctrl properties to support voltage-dependent pin configuration switching for UHS-I SD card modes. Add optional pinctrl-names property with two states: - "default": For 3.3V operation with standard drive strength - "state_uhs": For 1.8V operation with optimized drive strength These pinctrl states allow the SDHCI driver to coordinate voltage switching with pin configuration changes, ensuring proper signal integrity during UHS-I mode transitions. Acked-by: Conor Dooley Signed-off-by: Iker Pedrosa Signed-off-by: Ulf Hansson (cherry picked from commit 04a8ddc39e5cfebffb06f383350a6ee393ba89a6) Signed-off-by: Han Gao --- .../devicetree/bindings/mmc/spacemit,sdhci.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml index 9a055d963a7f0c..34d202af909f2d 100644 --- a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml @@ -44,6 +44,18 @@ properties: - const: axi - const: sdh + pinctrl-names: + minItems: 1 + items: + - const: default + - const: uhs + + pinctrl-0: + description: Default pinctrl state for 3.3V operation + + pinctrl-1: + description: Optional pinctrl state for 1.8V UHS operation with "uhs" name + required: - compatible - reg @@ -62,4 +74,7 @@ examples: interrupt-parent = <&plic>; clocks = <&clk_apmu 10>, <&clk_apmu 13>; clock-names = "core", "io"; + pinctrl-names = "default", "uhs"; + pinctrl-0 = <&sdhci_default_cfg>; + pinctrl-1 = <&sdhci_uhs_cfg>; }; From 089fec6b582ae6e88671816c0d12278104ffca6f Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:57 +0200 Subject: [PATCH 061/521] UPSTREAM: mmc: sdhci-of-k1: enable essential clock infrastructure for SD operation Ensure SD card pins receive clock signals by enabling pad clock generation and overriding automatic clock gating. Required for all SD operation modes. The SDHC_GEN_PAD_CLK_ON setting in LEGACY_CTRL_REG is safe for both SD and eMMC operation as both protocols use the same physical MMC interface pins and require proper clock signal generation at the hardware level for signal integrity and timing. Additional SD-specific clock overrides (SDHC_OVRRD_CLK_OEN and SDHC_FORCE_CLK_ON) are conditionally applied only for SD-only controllers to handle removable card scenarios. Tested-by: Anand Moon Acked-by: Adrian Hunter Tested-by: Trevor Gamblin Reviewed-by: Troy Mitchell Tested-by: Vincent Legoll Signed-off-by: Iker Pedrosa Signed-off-by: Ulf Hansson (cherry picked from commit f87b273e4b6dfe57dcd63c24cbe3764d1f6954ae) Signed-off-by: Han Gao --- drivers/mmc/host/sdhci-of-k1.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index 455656f9842df9..0dd06fc19b8574 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -21,6 +21,13 @@ #include "sdhci.h" #include "sdhci-pltfm.h" +#define SPACEMIT_SDHC_OP_EXT_REG 0x108 +#define SDHC_OVRRD_CLK_OEN BIT(11) +#define SDHC_FORCE_CLK_ON BIT(12) + +#define SPACEMIT_SDHC_LEGACY_CTRL_REG 0x10C +#define SDHC_GEN_PAD_CLK_ON BIT(6) + #define SPACEMIT_SDHC_MMC_CTRL_REG 0x114 #define SDHC_MISC_INT_EN BIT(1) #define SDHC_MISC_INT BIT(2) @@ -101,6 +108,12 @@ static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask) if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) spacemit_sdhci_setbits(host, SDHC_MMC_CARD_MODE, SPACEMIT_SDHC_MMC_CTRL_REG); + + spacemit_sdhci_setbits(host, SDHC_GEN_PAD_CLK_ON, SPACEMIT_SDHC_LEGACY_CTRL_REG); + + if (host->mmc->caps2 & MMC_CAP2_NO_MMC) + spacemit_sdhci_setbits(host, SDHC_OVRRD_CLK_OEN | SDHC_FORCE_CLK_ON, + SPACEMIT_SDHC_OP_EXT_REG); } static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) From d01cd0e369767c06a73b070450a0046c124f881c Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:58 +0200 Subject: [PATCH 062/521] UPSTREAM: mmc: sdhci-of-k1: add regulator and pinctrl voltage switching support Add voltage switching infrastructure for UHS-I modes by integrating both regulator framework (for supply voltage control) and pinctrl state switching (for pin drive strength optimization). - Add regulator supply parsing and voltage switching callback - Add optional pinctrl state switching between "default" (3.3V) and "state_uhs" (1.8V) configurations - Enable coordinated voltage and pin configuration changes for UHS modes This provides complete voltage switching support while maintaining backward compatibility when pinctrl states are not defined. Tested-by: Anand Moon Tested-by: Trevor Gamblin Acked-by: Adrian Hunter Reviewed-by: Troy Mitchell Tested-by: Vincent Legoll Signed-off-by: Iker Pedrosa Signed-off-by: Ulf Hansson (cherry picked from commit 00a97fc57c09ff1cf71107753d9b5629caeb8c8a) Signed-off-by: Han Gao --- drivers/mmc/host/sdhci-of-k1.c | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index 0dd06fc19b8574..d9144537032a51 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "sdhci.h" @@ -71,6 +72,9 @@ struct spacemit_sdhci_host { struct clk *clk_core; struct clk *clk_io; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_default; + struct pinctrl_state *pinctrl_uhs; }; /* All helper functions will update clr/set while preserve rest bits */ @@ -219,6 +223,46 @@ static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc) SPACEMIT_SDHC_PHY_CTRL_REG); } +static int spacemit_sdhci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host); + struct pinctrl_state *state; + int ret; + + ret = sdhci_start_signal_voltage_switch(mmc, ios); + if (ret) + return ret; + + if (!sdhst->pinctrl) + return 0; + + /* Select appropriate pinctrl state based on signal voltage */ + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + state = sdhst->pinctrl_default; + break; + case MMC_SIGNAL_VOLTAGE_180: + state = sdhst->pinctrl_uhs; + break; + default: + dev_warn(mmc_dev(mmc), "unsupported voltage %d\n", ios->signal_voltage); + return 0; + } + + ret = pinctrl_select_state(sdhst->pinctrl, state); + if (ret) { + dev_warn(mmc_dev(mmc), "failed to select pinctrl state: %d\n", ret); + return 0; + } + dev_dbg(mmc_dev(mmc), "switched to %s pinctrl state\n", + ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180 ? "UHS" : "default"); + + return 0; +} + static inline int spacemit_sdhci_get_clocks(struct device *dev, struct sdhci_pltfm_host *pltfm_host) { @@ -252,6 +296,30 @@ static inline int spacemit_sdhci_get_resets(struct device *dev) return 0; } +static inline void spacemit_sdhci_get_pins(struct device *dev, + struct sdhci_pltfm_host *pltfm_host) +{ + struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host); + + sdhst->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(sdhst->pinctrl)) { + sdhst->pinctrl = NULL; + dev_dbg(dev, "pinctrl not available, voltage switching will work without it\n"); + return; + } + + sdhst->pinctrl_default = pinctrl_lookup_state(sdhst->pinctrl, "default"); + if (IS_ERR(sdhst->pinctrl_default)) + sdhst->pinctrl_default = NULL; + + sdhst->pinctrl_uhs = pinctrl_lookup_state(sdhst->pinctrl, "uhs"); + if (IS_ERR(sdhst->pinctrl_uhs)) + sdhst->pinctrl_uhs = NULL; + + dev_dbg(dev, "pinctrl setup: default=%p, uhs=%p\n", + sdhst->pinctrl_default, sdhst->pinctrl_uhs); +} + static const struct sdhci_ops spacemit_sdhci_ops = { .get_max_clock = spacemit_sdhci_clk_get_max_clock, .reset = spacemit_sdhci_reset, @@ -324,6 +392,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev) host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY; + spacemit_sdhci_get_pins(dev, pltfm_host); + + host->mmc_host_ops.start_signal_voltage_switch = spacemit_sdhci_start_signal_voltage_switch; + ret = spacemit_sdhci_get_clocks(dev, pltfm_host); if (ret) goto err_pltfm; From adbf5ca39fd3f1f2ebda0b3ea79371017f96d9e9 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:59 +0200 Subject: [PATCH 063/521] UPSTREAM: mmc: sdhci-of-k1: add comprehensive SDR tuning support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement software tuning algorithm to enable UHS-I SDR modes for SD card operation and HS200 mode for eMMC. This adds both TX and RX delay line tuning based on the SpacemiT K1 controller capabilities. Algorithm features: - Add tuning register definitions (RX_CFG, DLINE_CTRL, DLINE_CFG) - Conditional tuning: only for high-speed modes (≥100MHz) - TX tuning: configure transmit delay line with optimal values (dline_reg=0, delaycode=127) to ensure optimal signal output timing - RX tuning: single-pass window detection algorithm testing full delay range (0-255) to find optimal receive timing window - Retry mechanism: multiple fallback delays within optimal window for improved reliability Tested-by: Anand Moon Acked-by: Adrian Hunter Tested-by: Trevor Gamblin Tested-by: Vincent Legoll Signed-off-by: Iker Pedrosa Signed-off-by: Ulf Hansson (cherry picked from commit e9cb83c10071808aa7db4582e007a650aa6aa183) Signed-off-by: Han Gao --- drivers/mmc/host/sdhci-of-k1.c | 172 +++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index d9144537032a51..37b0911e7cf202 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -69,6 +69,28 @@ #define SDHC_PHY_DRIVE_SEL GENMASK(2, 0) #define SDHC_RX_BIAS_CTRL BIT(5) +#define SPACEMIT_SDHC_RX_CFG_REG 0x118 +#define SDHC_RX_SDCLK_SEL0_MASK GENMASK(1, 0) +#define SDHC_RX_SDCLK_SEL1_MASK GENMASK(3, 2) +#define SDHC_RX_SDCLK_SEL1 FIELD_PREP(SDHC_RX_SDCLK_SEL1_MASK, 1) + +#define SPACEMIT_SDHC_DLINE_CTRL_REG 0x130 +#define SDHC_DLINE_PU BIT(0) +#define SDHC_RX_DLINE_CODE_MASK GENMASK(23, 16) +#define SDHC_TX_DLINE_CODE_MASK GENMASK(31, 24) + +#define SPACEMIT_SDHC_DLINE_CFG_REG 0x134 +#define SDHC_RX_DLINE_REG_MASK GENMASK(7, 0) +#define SDHC_RX_DLINE_GAIN BIT(8) +#define SDHC_TX_DLINE_REG_MASK GENMASK(23, 16) + +#define SPACEMIT_RX_DLINE_REG 9 +#define SPACEMIT_RX_TUNE_DELAY_MIN 0x0 +#define SPACEMIT_RX_TUNE_DELAY_MAX 0xFF + +#define SPACEMIT_TX_TUNING_DLINE_REG 0x00 +#define SPACEMIT_TX_TUNING_DELAYCODE 127 + struct spacemit_sdhci_host { struct clk *clk_core; struct clk *clk_io; @@ -96,6 +118,50 @@ static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u sdhci_writel(host, val, reg); } +static void spacemit_sdhci_set_rx_delay(struct sdhci_host *host, u8 delay) +{ + spacemit_sdhci_clrsetbits(host, SDHC_RX_DLINE_CODE_MASK, + FIELD_PREP(SDHC_RX_DLINE_CODE_MASK, delay), + SPACEMIT_SDHC_DLINE_CTRL_REG); +} + +static void spacemit_sdhci_set_tx_delay(struct sdhci_host *host, u8 delay) +{ + spacemit_sdhci_clrsetbits(host, SDHC_TX_DLINE_CODE_MASK, + FIELD_PREP(SDHC_TX_DLINE_CODE_MASK, delay), + SPACEMIT_SDHC_DLINE_CTRL_REG); +} + +static void spacemit_sdhci_set_tx_dline_reg(struct sdhci_host *host, u8 dline_reg) +{ + spacemit_sdhci_clrsetbits(host, SDHC_TX_DLINE_REG_MASK, + FIELD_PREP(SDHC_TX_DLINE_REG_MASK, dline_reg), + SPACEMIT_SDHC_DLINE_CFG_REG); +} + +static void spacemit_sdhci_tx_tuning_prepare(struct sdhci_host *host) +{ + spacemit_sdhci_setbits(host, SDHC_TX_MUX_SEL, SPACEMIT_SDHC_TX_CFG_REG); + spacemit_sdhci_setbits(host, SDHC_DLINE_PU, SPACEMIT_SDHC_DLINE_CTRL_REG); + udelay(5); +} + +static void spacemit_sdhci_prepare_tuning(struct sdhci_host *host) +{ + spacemit_sdhci_clrsetbits(host, SDHC_RX_DLINE_REG_MASK, + FIELD_PREP(SDHC_RX_DLINE_REG_MASK, SPACEMIT_RX_DLINE_REG), + SPACEMIT_SDHC_DLINE_CFG_REG); + + spacemit_sdhci_setbits(host, SDHC_DLINE_PU, SPACEMIT_SDHC_DLINE_CTRL_REG); + udelay(5); + + spacemit_sdhci_clrsetbits(host, SDHC_RX_SDCLK_SEL1_MASK, SDHC_RX_SDCLK_SEL1, + SPACEMIT_SDHC_RX_CFG_REG); + + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200) + spacemit_sdhci_setbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG); +} + static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask) { sdhci_reset(host, mask); @@ -191,6 +257,111 @@ static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host) return clk_get_rate(pltfm_host->clk); } +static int spacemit_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int current_len = 0, current_start = 0; + int max_pass_len = 0, max_pass_start = 0; + struct mmc_host *mmc = host->mmc; + struct mmc_ios ios = mmc->ios; + u8 final_delay; + int ret = 0; + int i; + + /* + * Tuning is required for SDR50/SDR104, HS200/HS400 cards and + * if clock frequency is greater than 100MHz in these modes. + */ + if (host->clock < 100 * 1000 * 1000 || + !(ios.timing == MMC_TIMING_MMC_HS200 || + ios.timing == MMC_TIMING_UHS_SDR50 || + ios.timing == MMC_TIMING_UHS_SDR104)) + return 0; + + if (mmc->caps2 & MMC_CAP2_NO_MMC) { + spacemit_sdhci_set_tx_dline_reg(host, SPACEMIT_TX_TUNING_DLINE_REG); + spacemit_sdhci_set_tx_delay(host, SPACEMIT_TX_TUNING_DELAYCODE); + spacemit_sdhci_tx_tuning_prepare(host); + + dev_dbg(mmc_dev(host->mmc), "TX tuning: dline_reg=%d, delaycode=%d\n", + SPACEMIT_TX_TUNING_DLINE_REG, SPACEMIT_TX_TUNING_DELAYCODE); + } + + spacemit_sdhci_prepare_tuning(host); + + for (i = SPACEMIT_RX_TUNE_DELAY_MIN; i <= SPACEMIT_RX_TUNE_DELAY_MAX; i++) { + spacemit_sdhci_set_rx_delay(host, i); + ret = mmc_send_tuning(host->mmc, opcode, NULL); + + dev_dbg(mmc_dev(host->mmc), "RX delay %d: %s\n", + i, ret == 0 ? "pass" : "fail"); + + if (ret == 0) { + /* Test passed - extend current window */ + if (current_len == 0) + current_start = i; + current_len++; + } else { + /* Test failed - check if current window is best so far */ + if (current_len > max_pass_len) { + max_pass_len = current_len; + max_pass_start = current_start; + } + current_len = 0; + } + } + + if (current_len > max_pass_len) { + max_pass_len = current_len; + max_pass_start = current_start; + } + + if (max_pass_len < 3) { + dev_err(mmc_dev(host->mmc), "Tuning failed: no stable window found\n"); + return -EIO; + } + + final_delay = max_pass_start + max_pass_len / 2; + spacemit_sdhci_set_rx_delay(host, final_delay); + ret = mmc_send_tuning(host->mmc, opcode, NULL); + if (ret) { + u8 retry_delays[] = { + max_pass_start + max_pass_len / 4, + max_pass_start + (3 * max_pass_len) / 4, + max_pass_start, + max_pass_start + max_pass_len - 1 + }; + int retry_count = ARRAY_SIZE(retry_delays); + + dev_warn(mmc_dev(mmc), "Primary delay %d failed, trying alternatives\n", + final_delay); + + for (i = 0; i < retry_count; i++) { + if (retry_delays[i] >= SPACEMIT_RX_TUNE_DELAY_MIN && + retry_delays[i] <= SPACEMIT_RX_TUNE_DELAY_MAX) { + spacemit_sdhci_set_rx_delay(host, retry_delays[i]); + ret = mmc_send_tuning(host->mmc, opcode, NULL); + if (!ret) { + final_delay = retry_delays[i]; + dev_info(mmc_dev(mmc), "Retry successful with delay %d\n", + final_delay); + break; + } + } + } + + if (ret) { + dev_err(mmc_dev(mmc), "All retry attempts failed\n"); + return -EIO; + } + } + + dev_dbg(mmc_dev(host->mmc), + "Tuning successful: window %d-%d, using delay %d\n", + max_pass_start, max_pass_start + max_pass_len - 1, final_delay); + + return 0; +} + static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); @@ -326,6 +497,7 @@ static const struct sdhci_ops spacemit_sdhci_ops = { .set_bus_width = sdhci_set_bus_width, .set_clock = spacemit_sdhci_set_clock, .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling, + .platform_execute_tuning = spacemit_sdhci_execute_tuning, }; static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = { From a814823cde5adb5fb493eb47e3dc444eddfa4867 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 15 May 2026 12:48:59 +0200 Subject: [PATCH 064/521] UPSTREAM: riscv: dts: spacemit: k1: add SD card controller and pinctrl support Add SD card controller infrastructure for SpacemiT K1 SoC with complete pinctrl support for both standard and UHS modes. - Add sdhci0 controller definition with clocks, resets and interrupts - Add mmc1_cfg pinctrl for 3.3V standard SD operation - Add mmc1_uhs_cfg pinctrl for 1.8V UHS high-speed operation - Configure appropriate drive strength and power-source properties This provides complete SD card infrastructure that K1-based boards can enable. Tested-by: Anand Moon Tested-by: Trevor Gamblin Tested-by: Vincent Legoll Reviewed-by: Troy Mitchell Signed-off-by: Iker Pedrosa Link: https://patch.msgid.link/20260515-orangepi-sd-card-uhs-v10-1-094af27e310d@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit 2d77e577109967ef65f269c1fc1d6a659d4260fb) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi | 40 ++++++++++++++++++++ arch/riscv/boot/dts/spacemit/k1.dtsi | 13 +++++++ 2 files changed, 53 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi index 34d88334e95e42..4e9a62d0e85b5d 100644 --- a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi @@ -590,4 +590,44 @@ power-source = <3300>; }; }; + + mmc1_cfg: mmc1-cfg { + mmc1-data-cmd-pins { + pinmux = , /* mmc1_d3 */ + , /* mmc1_d2 */ + , /* mmc1_d1 */ + , /* mmc1_d0 */ + ; /* mmc1_cmd */ + bias-pull-up = <1>; + drive-strength = <19>; + power-source = <3300>; + }; + + mmc1-clk-pins { + pinmux = ; /* mmc1_clk */ + bias-pull-down = <1>; + drive-strength = <19>; + power-source = <3300>; + }; + }; + + mmc1_uhs_cfg: mmc1-uhs-cfg { + mmc1-data-cmd-pins { + pinmux = , /* mmc1_d3 */ + , /* mmc1_d2 */ + , /* mmc1_d1 */ + , /* mmc1_d0 */ + ; /* mmc1_cmd */ + bias-pull-up = <1>; + drive-strength = <42>; + power-source = <1800>; + }; + + mmc1-clk-pins { + pinmux = ; /* mmc1_clk */ + bias-pull-down = <1>; + drive-strength = <42>; + power-source = <1800>; + }; + }; }; diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index cba160c5edc5d6..08a0f28d011fea 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -1315,6 +1315,19 @@ status = "disabled"; }; + sdhci0: mmc@d4280000 { + compatible = "spacemit,k1-sdhci"; + reg = <0x0 0xd4280000 0x0 0x200>; + clocks = <&syscon_apmu CLK_SDH_AXI>, + <&syscon_apmu CLK_SDH0>; + clock-names = "core", "io"; + resets = <&syscon_apmu RESET_SDH_AXI>, + <&syscon_apmu RESET_SDH0>; + reset-names = "axi", "sdh"; + interrupts = <99>; + status = "disabled"; + }; + emmc: mmc@d4281000 { compatible = "spacemit,k1-sdhci"; reg = <0x0 0xd4281000 0x0 0x200>; From fe6c79e3ead1147d7984c4a45f8db9592a1a8d0e Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 15 May 2026 12:49:00 +0200 Subject: [PATCH 065/521] UPSTREAM: riscv: dts: spacemit: k1-orangepi-rv2: add SD card support with UHS modes Add complete SD card controller support with UHS high-speed modes. - Enable sdhci0 controller with 4-bit bus width - Configure card detect GPIO with GPIO_ACTIVE_LOW logic - Connect vmmc-supply to buck4 for 3.3V card power - Connect vqmmc-supply to aldo1 for 1.8V/3.3V I/O switching - Add dual pinctrl states for voltage-dependent pin configuration - Support UHS-I SDR25, SDR50, and SDR104 modes - Add stable MMC device aliases (mmc0 = eMMC, mmc1 = SD card) This enables full SD card functionality including high-speed UHS modes for improved performance. Tested-by: Anand Moon Tested-by: Trevor Gamblin Tested-by: Michael Opdenacker Tested-by: Vincent Legoll Signed-off-by: Iker Pedrosa Link: https://patch.msgid.link/20260515-orangepi-sd-card-uhs-v10-2-094af27e310d@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit 2585c60ce2f977b13f14a67d1e3ed9c73fd7f381) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-orangepi-rv2.dts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index c95ca38e3d4a1e..bd40bc9011e298 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -17,6 +17,8 @@ serial0 = &uart0; ethernet0 = ð0; ethernet1 = ð1; + mmc0 = &emmc; + mmc1 = &sdhci0; }; chosen { @@ -202,7 +204,7 @@ regulator-always-on; }; - aldo1 { + aldo1: aldo1 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3400000>; regulator-boot-on; @@ -319,3 +321,21 @@ vdd-supply = <&vcc_5v0>; }; }; + +&sdhci0 { + pinctrl-names = "default", "uhs"; + pinctrl-0 = <&mmc1_cfg>; + pinctrl-1 = <&mmc1_uhs_cfg>; + bus-width = <4>; + cd-gpios = <&gpio K1_GPIO(80) GPIO_ACTIVE_LOW>; + no-mmc; + no-sdio; + disable-wp; + cap-sd-highspeed; + vmmc-supply = <&buck4_3v3>; + vqmmc-supply = <&aldo1>; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + status = "okay"; +}; From a554b173f5442b3649a68a7fda49276c4efcd83e Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 15 May 2026 12:49:01 +0200 Subject: [PATCH 066/521] UPSTREAM: riscv: dts: spacemit: k1-bananapi-f3: add SD card support with UHS modes Add complete SD card controller support with UHS high-speed modes. - Enable sdhci0 controller with 4-bit bus width - Configure card detect GPIO with GPIO_ACTIVE_LOW and internal pull-up support - Connect vmmc-supply to buck4 for 3.3V card power - Connect vqmmc-supply to aldo1 for 1.8V/3.3V I/O switching - Add dual pinctrl states for voltage-dependent pin configuration - Support UHS-I SDR25, SDR50, and SDR104 modes - Add stable MMC device aliases (mmc0 = eMMC, mmc1 = SD card) This enables full SD card functionality including high-speed UHS modes for improved performance. Suggested-by: Anand Moon Tested-by: Anand Moon Tested-by: Margherita Milani Tested-by: Aurelien Jarno Reviewed-by: Aurelien Jarno Signed-off-by: Iker Pedrosa Link: https://patch.msgid.link/20260515-orangepi-sd-card-uhs-v10-3-094af27e310d@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit c76e2f058cbcab84e4a703f26857a58bdf6a0042) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-bananapi-f3.dts | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index e20daa50a152f3..72f77e9edd63db 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -17,6 +17,8 @@ spi3 = &spi3; i2c2 = &i2c2; i2c8 = &i2c8; + mmc0 = &emmc; + mmc1 = &sdhci0; }; chosen { @@ -221,7 +223,7 @@ regulator-always-on; }; - buck4 { + buck4: buck4 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3300000>; regulator-ramp-delay = <5000>; @@ -242,7 +244,7 @@ regulator-always-on; }; - aldo1 { + aldo1: aldo1 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3400000>; regulator-boot-on; @@ -374,3 +376,21 @@ reset-gpios = <&gpio K1_GPIO(124) GPIO_ACTIVE_LOW>; }; }; + +&sdhci0 { + pinctrl-names = "default", "uhs"; + pinctrl-0 = <&mmc1_cfg>; + pinctrl-1 = <&mmc1_uhs_cfg>; + bus-width = <4>; + cd-gpios = <&gpio K1_GPIO(80) (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + no-mmc; + no-sdio; + disable-wp; + cap-sd-highspeed; + vmmc-supply = <&buck4>; + vqmmc-supply = <&aldo1>; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + status = "okay"; +}; From 32821940c46e77c5236154b02c74f8061b0ade03 Mon Sep 17 00:00:00 2001 From: Trevor Gamblin Date: Wed, 20 May 2026 15:06:23 +0200 Subject: [PATCH 067/521] UPSTREAM: riscv: dts: spacemit: k1-musepi-pro: add SD card support with UHS modes Update the Muse Pi Pro devicetree with SD card support to match what was done for the OrangePi RV2 in [1]. More precisely: - Enable sdhci0 controller with 4-bit bus width - Configure card detect GPIO with internal pull-up support - Connect vmmc-supply to buck4 for 3.3V card power - Connect vqmmc-supply to aldo1 for 1.8V/3.3V I/O switching - Add dual pinctrl states for voltage-dependent pin configuration - Support UHS-I SDR25, SDR50, and SDR104 modes - Add stable MMC device aliases (mmc0 = eMMC, mmc1 = SD card) [1] https://lore.kernel.org/linux-riscv/20260316-orangepi-sd-card-uhs-v3-0-aefd3b7832df@gmail.com/T/# Tested-by: Andre Heider Signed-off-by: Trevor Gamblin Signed-off-by: Iker Pedrosa Signed-off-by: Andre Heider Link: https://lore.kernel.org/linux-riscv/20260316-orangepi-sd-card-uhs-v3-0-aefd3b7832df@gmail.com/T/# Link: https://patch.msgid.link/20260520130624.1763487-1-a.heider@gmail.com Signed-off-by: Yixun Lan (cherry picked from commit 85e4c5733cd7efa39f10d7200095c016ce4a2815) Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k1-musepi-pro.dts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index 4aef287353cab6..0d809e4ad3b1f7 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -19,6 +19,8 @@ serial0 = &uart0; i2c2 = &i2c2; i2c8 = &i2c8; + mmc0 = &emmc; + mmc1 = &sdhci0; }; chosen { @@ -218,7 +220,7 @@ regulator-always-on; }; - aldo1 { + aldo1: aldo1 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3400000>; regulator-boot-on; @@ -384,3 +386,21 @@ reset-gpios = <&gpio K1_GPIO(123) GPIO_ACTIVE_LOW>; /* HUB_RST */ }; }; + +&sdhci0 { + pinctrl-names = "default", "uhs"; + pinctrl-0 = <&mmc1_cfg>; + pinctrl-1 = <&mmc1_uhs_cfg>; + bus-width = <4>; + cd-gpios = <&gpio K1_GPIO(80) (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; + no-mmc; + no-sdio; + disable-wp; + cap-sd-highspeed; + vmmc-supply = <&buck4_3v3>; + vqmmc-supply = <&aldo1>; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + status = "okay"; +}; From 20b48e1ed8c21f152661d1bf4a439a040f008cd5 Mon Sep 17 00:00:00 2001 From: MoeLeak Date: Fri, 8 May 2026 19:44:13 +0800 Subject: [PATCH 068/521] UPSTREAM: riscv: dts: thead: Add TH1520 I2C1 controller Describe the TH1520 I2C1 controller so boards can enable devices attached to that bus. The controller is disabled by default because pinmuxing and connected peripherals are board-specific. Signed-off-by: MoeLeak Signed-off-by: Drew Fustini (cherry picked from commit 2f60e351633044dee88b63fb1600e284d2476e9c) Signed-off-by: Han Gao --- arch/riscv/boot/dts/thead/th1520.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index 5e91dc1d2b9b7e..94932c51b7e392 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -411,6 +411,17 @@ status = "disabled"; }; + i2c1: i2c@ffe7f24000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xe7f24000 0x0 0x4000>; + interrupts = <45 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C1>, <&clk CLK_PERI_APB_PCLK>; + clock-names = "ref", "pclk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + gpio@ffe7f34000 { compatible = "snps,dw-apb-gpio"; reg = <0xff 0xe7f34000 0x0 0x1000>; From 107c790d1bfa2740a6bb6f44617e0e0980605049 Mon Sep 17 00:00:00 2001 From: MoeLeak Date: Fri, 8 May 2026 19:44:14 +0800 Subject: [PATCH 069/521] UPSTREAM: riscv: dts: thead: Enable WiFi on Lichee Pi 4A The Lichee Pi 4A has an RTL8723DS WiFi module connected to the TH1520 SDIO1 controller. The module reset line is driven through a PCA9557 GPIO expander on the I2C1 bus. Enable I2C1 for the GPIO expander and configure SDIO1 as a non-removable 4-bit SDIO bus using an mmc-pwrseq-simple reset sequence so the WiFi device can be powered and enumerated. Signed-off-by: MoeLeak Signed-off-by: Drew Fustini (cherry picked from commit a20241c08385c875c4d0deb6c8ffc15f13f5b388) Signed-off-by: Han Gao --- .../boot/dts/thead/th1520-lichee-pi-4a.dts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts index 7cb7d28683bce7..af6f25ebbf6071 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts @@ -4,6 +4,7 @@ */ #include "th1520-lichee-module-4a.dtsi" +#include / { model = "Sipeed Lichee Pi 4A"; @@ -40,6 +41,12 @@ }; }; + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&ioexp2 4 GPIO_ACTIVE_LOW>; + post-power-on-delay-ms = <200>; + }; + thermal-zones { cpu-thermal { polling-delay = <1000>; @@ -96,6 +103,20 @@ }; +&padctrl1_apsys { + i2c1_pins: i2c1-0 { + i2c-pins { + pins = "I2C1_SCL", "I2C1_SDA"; + function = "i2c"; + bias-pull-up = <2100>; + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; +}; + &padctrl0_apsys { fan_pins: fan-0 { pwm1-pins { @@ -132,6 +153,32 @@ }; }; +&i2c1 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; + + ioexp2: gpio@18 { + compatible = "nxp,pca9557"; + reg = <0x18>; + gpio-controller; + #gpio-cells = <2>; + }; +}; + +&sdio1 { + #address-cells = <1>; + #size-cells = <0>; + bus-width = <4>; + max-frequency = <198000000>; + cap-sdio-irq; + keep-power-in-suspend; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + status = "okay"; +}; + &dpu { status = "okay"; }; From e276582d20aefbb72db52f373ea8cd11b978ad4d Mon Sep 17 00:00:00 2001 From: Thomas Gerner Date: Thu, 14 May 2026 20:32:01 +0200 Subject: [PATCH 070/521] UPSTREAM: riscv: dts: thead: Enable wifi on the BeagleV-Ahead The BeagleV-Ahead board uses an AP6203BM WiFi chip from AMPAK Technology Inc. connected to SDIO1. The chip is compatible to the broadcom wireless driver. The AP6203BM is a dual-band 2.4GHz/5GHz Wi-Fi 4 (802.11a/b/g/n) and Bluetooth 5.4 module. Bluetooth is not enabled by this patch. Signed-off-by: Thomas Gerner Signed-off-by: Drew Fustini (cherry picked from commit 3a5791956edbfa84d7256224167941931cbc46e7) Signed-off-by: Han Gao --- .../boot/dts/thead/th1520-beaglev-ahead.dts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts b/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts index 91f3f9b987bc38..e16484a476534c 100644 --- a/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts +++ b/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts @@ -86,6 +86,11 @@ }; }; }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio2 31 GPIO_ACTIVE_LOW>; /* WL-REG-ON */ + }; }; &osc { @@ -239,6 +244,28 @@ slew-rate = <0>; }; }; + + wifi_pins: wifi-0 { + host-wake-pins { + pins = "GPIO2_25"; + function = "gpio"; + bias-disable; + drive-strength = <1>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + + reg-on-pins { + pins = "GPIO2_31"; + function = "gpio"; + bias-disable; + drive-strength = <3>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; }; &sdio0 { @@ -247,6 +274,27 @@ status = "okay"; }; +&sdio1 { + bus-width = <4>; + max-frequency = <198000000>; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_pins>; + #address-cells = <1>; + #size-cells = <0>; + non-removable; + keep-power-in-suspend; + mmc-pwrseq = <&wifi_pwrseq>; + status = "okay"; + + wifi@1 { + compatible = "cypress,cyw43012-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + interrupt-parent = <&gpio2>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "host-wake"; + }; +}; + &dpu { status = "okay"; }; From bec450b0a671bb8fba26c3655f79483156dfe222 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 071/521] UPSTREAM: riscv: module: Use generic cmp_int() instead of custom cmp_3way() The module-sections.c file defines a custom cmp_3way() macro to perform 3-way comparisons during relocation sorting. Instead of maintaining our own implementation, use the generic cmp_int() macro provided by the already included . This removes redundant code and relies on standard kernel interfaces. Signed-off-by: Florian Schmaus Link: https://patch.msgid.link/20260512063231.708256-1-florian.schmaus@codasip.com Signed-off-by: Paul Walmsley (cherry picked from commit 3e17a4b443bbaecc723a2d51faeaa1a0097e43e9) Signed-off-by: Han Gao --- arch/riscv/kernel/module-sections.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c index 98eaac6f660604..b3b11b7f7ed958 100644 --- a/arch/riscv/kernel/module-sections.c +++ b/arch/riscv/kernel/module-sections.c @@ -56,17 +56,15 @@ unsigned long module_emit_plt_entry(struct module *mod, unsigned long val) return (unsigned long)&plt[i]; } -#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b)) - static int cmp_rela(const void *a, const void *b) { const Elf_Rela *x = a, *y = b; int i; /* sort by type, symbol index and addend */ - i = cmp_3way(x->r_info, y->r_info); + i = cmp_int(x->r_info, y->r_info); if (i == 0) - i = cmp_3way(x->r_addend, y->r_addend); + i = cmp_int(x->r_addend, y->r_addend); return i; } From ca58235da1ea6cc90eada4d61c49b9bdd865eb20 Mon Sep 17 00:00:00 2001 From: Fangyu Yu Date: Fri, 17 Apr 2026 22:07:45 +0800 Subject: [PATCH 072/521] UPSTREAM: iommu/riscv: Advertise Svpbmt support to generic page table The RISC-V IOMMU can optionally support Svpbmt page-based memory types in its page table format. When present,the generic page table code can use this capability to encode memory attributes (e.g. MMIO vs normal memory) in PTEs. Signed-off-by: Fangyu Yu Reviewed-by: Jason Gunthorpe Reviewed-by: Anup Patel Reviewed-by: Guo Ren Reviewed-by: Nutty Liu Reviewed-by: Kevin Tian Signed-off-by: Joerg Roedel (cherry picked from commit f196a86687974cfcc1e8cade99ffca4605141860) Signed-off-by: Han Gao --- drivers/iommu/riscv/iommu.c | 2 ++ include/linux/generic_pt/common.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c index a31f50bbad3535..6c324f9fdc5347 100644 --- a/drivers/iommu/riscv/iommu.c +++ b/drivers/iommu/riscv/iommu.c @@ -1268,6 +1268,8 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev) cfg.common.features = BIT(PT_FEAT_SIGN_EXTEND) | BIT(PT_FEAT_FLUSH_RANGE) | BIT(PT_FEAT_RISCV_SVNAPOT_64K); + if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SVPBMT) + cfg.common.features |= BIT(PT_FEAT_RISCV_SVPBMT); domain->riscvpt.iommu.nid = dev_to_node(iommu->dev); domain->domain.ops = &riscv_iommu_paging_domain_ops; diff --git a/include/linux/generic_pt/common.h b/include/linux/generic_pt/common.h index fc5d0b5edadc08..2683e5b3899874 100644 --- a/include/linux/generic_pt/common.h +++ b/include/linux/generic_pt/common.h @@ -188,6 +188,10 @@ enum { * Support the 64k contiguous page size following the Svnapot extension. */ PT_FEAT_RISCV_SVNAPOT_64K = PT_FEAT_FMT_START, + /* + * Support Svpbmt extension: encode page-based memory type (PBMT) in PTEs. + */ + PT_FEAT_RISCV_SVPBMT, }; From 983520af94579cd3f732aeef9303380c4a611175 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 073/521] UPSTREAM: riscv: propagate insert_resource result from add_resource Currently, add_resource() returns 1 on success, even though its callers only check for negative values. Instead, propagate the insert_resource() result from add_resource() to align with standard kernel return-value conventions (0 on success, negative errno on failure). Use %pR to print the full resource range while at it. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260512172034.328405-4-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley (cherry picked from commit 13fe217f64a7ee71aa58d871c6021a721a5a0c6f) Signed-off-by: Han Gao --- arch/riscv/kernel/setup.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index c89cc272440b9f..52d1d2b8f338b8 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -71,16 +71,13 @@ static struct resource *standard_resources; static int __init add_resource(struct resource *parent, struct resource *res) { - int ret = 0; + int ret; ret = insert_resource(parent, res); - if (ret < 0) { - pr_err("Failed to add a %s resource at %llx\n", - res->name, (unsigned long long) res->start); - return ret; - } + if (ret < 0) + pr_err("Failed to add resource %s %pR\n", res->name, res); - return 1; + return ret; } static int __init add_kernel_resources(void) From e387032851a0f06dfdd75e4674293ec6067ea7bd Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Sat, 16 May 2026 16:00:30 +0800 Subject: [PATCH 074/521] UPSTREAM: riscv: dts: spacemit: enable QSPI for OrangePi RV2 Enable the QSPI controller and the XM25QU128C SPI NOR flash on the OrangePi RV2 board. Add a flash partition layout from vendor UBoot. Signed-off-by: Chukun Pan Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260516080030.1736836-1-amadeus@jmu.edu.cn Signed-off-by: Yixun Lan (cherry picked from commit e2518e8cb1dde64af2d1bb246639bb7ef7523f7a) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-orangepi-rv2.dts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index bd40bc9011e298..7c49bce427f307 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -290,6 +290,51 @@ status = "okay"; }; +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + vcc-supply = <&buck3_1v8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootinfo@0 { + reg = <0x00000 0x010000>; + }; + + private@10000 { + reg = <0x10000 0x010000>; + }; + + fsbl@20000 { + reg = <0x20000 0x040000>; + }; + + env@60000 { + reg = <0x60000 0x010000>; + }; + + opensbi@70000 { + reg = <0x70000 0x030000>; + }; + + uboot@a0000 { + reg = <0xa0000 0x760000>; + }; + }; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_2_cfg>; From 15550db3c6ac942d53dbf9cf12b1b32b6fc4660b Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 20:58:16 +0000 Subject: [PATCH 075/521] UPSTREAM: dts: riscv: spacemit: k3: Fix I/O power settings SpacemiT K3 SoC support dual-voltage I/O power domain, while initially configure to 3.3v, and need to access register from APBC space to switch to 1.8v domain. Fix the GMAC0's I/O pins 1.8v switch failure that will result a broken ethernet driver. Fixes: d8944577496b ("riscv: dts: spacemit: k3: add pinctrl support") Reported-by: Han Gao Link: https://patch.msgid.link/20260518-07-dts-pinctrl-io-power-v1-1-abe19c14a726@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit 3ea695eb111fc5a7ffddd21d5c3fee6d82560413) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 97cc9e2dd55467..7e3cdd42ab8e3b 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -1034,6 +1034,7 @@ clocks = <&syscon_apbc CLK_APBC_AIB>, <&syscon_apbc CLK_APBC_AIB_BUS>; clock-names = "func", "bus"; + spacemit,apbc = <&syscon_apbc>; }; uart10: serial@d401f000 { From f9ce76c6aedf48d8cb75b7009a998d933ddfc267 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:35 +0200 Subject: [PATCH 076/521] UPSTREAM: riscv: dts: spacemit: set console baud rate on Milk-V Jupiter Because the default console's baud rate is not set, defconfig kernels do not have any serial output on this platform. Set the baud rate to 115200, matching what is used by U-Boot etc on this platform. See-also: 24c12ca43b12c ("dts: spacemit: set console baud rate on bpif3") Fixes: 5b90a3d6092d9 ("riscv: dts: spacemit: Add Milk-V Jupiter board device tree") Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-2-aurelien@aurel32.net Signed-off-by: Yixun Lan (cherry picked from commit 4f97acb4f744f516bbe976da8282c0fe213216ff) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index afaad59e6bce22..db98dbfadf00ff 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -20,7 +20,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; leds { From f2231640421ed86c5d1f713e835d047c41b3a313 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:36 +0200 Subject: [PATCH 077/521] UPSTREAM: riscv: dts: spacemit: sort aliases on Milk-V Jupiter Before adding more aliases, just sort them. Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-3-aurelien@aurel32.net Signed-off-by: Yixun Lan (cherry picked from commit 9cdeba29d65b10e7bdb1491167141ff2df238ab8) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index db98dbfadf00ff..fbba3399234785 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -14,9 +14,9 @@ aliases { ethernet0 = ð0; ethernet1 = ð1; - serial0 = &uart0; i2c2 = &i2c2; i2c8 = &i2c8; + serial0 = &uart0; }; chosen { From ca0930376b5f41fdf6a0f1065c5ac64ef00bb96f Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:37 +0200 Subject: [PATCH 078/521] UPSTREAM: riscv: dts: spacemit: enable eMMC on Milk-V Jupiter The Milk-V Jupiter board has a connector for an eMMC module. Add an entry for it in the device tree and alias it mmc0. Mark the device as non-removable as eMMC modules have no CD pin and are not supposed to be inserted or removed while the system is running. On systems without an eMMC module installed, the kernel emits the following informational message during boot: mmc0: SDHCI controller on d4281000.mmc [d4281000.mmc] using ADMA mmc0: Failed to initialize a non-removable card Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-4-aurelien@aurel32.net Signed-off-by: Yixun Lan (cherry picked from commit 4bc9f44d581be66718cf7cf6e649fb92b3c2e6c6) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index fbba3399234785..1a833e716cb68e 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -16,6 +16,7 @@ ethernet1 = ð1; i2c2 = &i2c2; i2c8 = &i2c8; + mmc0 = &emmc; serial0 = &uart0; }; @@ -105,6 +106,16 @@ status = "okay"; }; +&emmc { + bus-width = <8>; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + non-removable; + no-sd; + no-sdio; + status = "okay"; +}; + ð0 { phy-handle = <&rgmii0>; phy-mode = "rgmii-id"; From ddc036f509303d966fdda7bf7d8c093fadc28d6e Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:38 +0200 Subject: [PATCH 079/521] UPSTREAM: riscv: dts: spacemit: enable SD card support on Milk-V Jupiter Add complete SD card controller support with UHS high-speed modes. - Enable sdhci0 controller with 4-bit bus width - Configure card detect GPIO with pull-up - Connect vmmc-supply to buck4 for 3.3V card power - Connect vqmmc-supply to aldo1 for 1.8V/3.3V I/O switching - Add dual pinctrl states for voltage-dependent pin configuration - Support UHS-I SDR25, SDR50, and SDR104 modes - Alias it as mmc1 Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-5-aurelien@aurel32.net Signed-off-by: Yixun Lan (cherry picked from commit 60b5e027c8c8cab69ad9cc7cab71ee795963e7b1) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-milkv-jupiter.dts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index 1a833e716cb68e..beebf804197e2b 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -17,6 +17,7 @@ i2c2 = &i2c2; i2c8 = &i2c8; mmc0 = &emmc; + mmc1 = &sdhci0; serial0 = &uart0; }; @@ -250,7 +251,7 @@ regulator-always-on; }; - aldo1 { + aldo1: aldo1 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3400000>; regulator-boot-on; @@ -385,6 +386,24 @@ }; }; +&sdhci0 { + pinctrl-names = "default", "uhs"; + pinctrl-0 = <&mmc1_cfg>; + pinctrl-1 = <&mmc1_uhs_cfg>; + bus-width = <4>; + cd-gpios = <&gpio K1_GPIO(80) (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + no-mmc; + no-sdio; + disable-wp; + cap-sd-highspeed; + vmmc-supply = <&buck4_3v3>; + vqmmc-supply = <&aldo1>; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_2_cfg>; From 3709d4e6ce5bb4df50c61fd417da148916ea38f9 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:39 +0200 Subject: [PATCH 080/521] UPSTREAM: riscv: dts: spacemit: fix uboot partition offset on Milk-V Jupiter Correct the uboot partition node name to match its actual offset. Fixes: 2829823956f0 ("riscv: dts: spacemit: enable QSPI and add SPI NOR on Milk-V Jupiter") Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-6-aurelien@aurel32.net Signed-off-by: Yixun Lan (cherry picked from commit 6edd9a0d32e1ef81133b8cb5b3bb3157a44da4d1) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index beebf804197e2b..2fc8d653378637 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -379,7 +379,7 @@ opensbi@70000 { reg = <0x70000 0x30000>; }; - uboot@a00000 { + uboot@a0000 { reg = <0xa0000 0x760000>; }; }; From a5069bde4566010227f39ae4ec3f06c3af041fbf Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 20 May 2026 00:40:07 +0800 Subject: [PATCH 081/521] UPSTREAM: pinctrl: spacemit: fix NULL check in spacemit_pin_set_config spacemit_pin_set_config() looks up the per-pin descriptor with spacemit_get_pin() then checks the wrong variable for failure: const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); ... if (!pin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); pin is an unsigned int pin id, where 0 (GPIO_0 / gmac0_rxdv on K3) is a valid pin, so rejecting it here drops the PAD config write for the first pin of every group. On K3 Pico-ITX the GMAC RGMII group lists pin 0 as its first entry, so its drive-strength / bias configuration was silently ignored. The intended guard is against spacemit_get_pin() returning NULL when the pin id isn't in the SoC's pin table. Check spin instead, which both restores PAD setup for pin 0 and prevents a NULL deref on spin->pin. Fixes: a83c29e1d145 ("pinctrl: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Han Gao Reviewed-by: Troy Mitchell Reviewed-by: Yixun Lan Signed-off-by: Linus Walleij (cherry picked from commit 09c816e5c4d3a8d6d6e4b7537433e5e98505d934) Signed-off-by: Han Gao --- drivers/pinctrl/spacemit/pinctrl-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index b0be62b1c8161d..95024e2bb5a587 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -795,7 +795,7 @@ static int spacemit_pin_set_config(struct spacemit_pinctrl *pctrl, void __iomem *reg; unsigned int mux; - if (!pin) + if (!spin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); From 546be8a6e203de5b4ea23b12fedd02b4bc1dabfc Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 20 May 2026 00:55:46 +0800 Subject: [PATCH 082/521] UPSTREAM: riscv: also select ARCH_KEEP_MEMBLOCK if kexec is selected On RISC-V, also select ARCH_KEEP_MEMBLOCK if kexec is selected, not only if ACPI is selected. This is because kexec requires the memblock areas to be kept after boot to initialize the secondary kernel. This is needed for both Device Tree and ACPI platforms. Signed-off-by: Han Gao Link: https://patch.msgid.link/20260519165546.123105-1-gaohan@iscas.ac.cn [pjw@kernel.org: change to add the dependency on kexec, rather than making it unconditional; rewrite the patch description accordingly] Signed-off-by: Paul Walmsley (cherry picked from commit bce35135fecc7a73c60aaa9d2ec699ead1e32661) Signed-off-by: Han Gao --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 46a89225b9cbb3..f54001f9c328ce 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -55,7 +55,7 @@ config RISCV select ARCH_HAS_UBSAN select ARCH_HAS_VDSO_ARCH_DATA if HAVE_GENERIC_VDSO select ARCH_HAVE_NMI_SAFE_CMPXCHG - select ARCH_KEEP_MEMBLOCK if ACPI + select ARCH_KEEP_MEMBLOCK if ACPI || KEXEC select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE if 64BIT && MMU select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT From 44f295ecb38b448329208e564c4de4956c61b737 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Sat, 6 Jun 2026 20:17:58 -0600 Subject: [PATCH 083/521] UPSTREAM: riscv: kexec_file: Constrain segment placement to direct map When kexec_file_load places segments with buf_max=ULONG_MAX and top_down=true, they land at the highest available physical addresses. On RISC-V the size of the linear mapping is determined by the active VM mode: SV39 caps the direct map at roughly 128GB, while SV48/SV57 extend the range substantially further. When the installed physical memory exceeds the direct map size of the active mode, top-down placement puts DTB/initrd at physical addresses outside the linearly mapped region. The kexec'd kernel cannot reach them during early boot, triggering a page fault at memcmp in start_kernel. Fix by constraining buf_max to PFN_PHYS(max_low_pfn), which reflects the runtime direct map boundary for the active VM mode (SV39/SV48/ SV57). This keeps all kexec segments within the linearly mapped region while preserving the upstream top_down allocation strategy. Signed-off-by: Han Gao Link: https://patch.msgid.link/20260519170641.123517-1-gaohan@iscas.ac.cn Signed-off-by: Paul Walmsley (cherry picked from commit b67a1ee0db0094c6cc158b087be6c334ad881a41) Signed-off-by: Han Gao --- arch/riscv/kernel/machine_kexec_file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c index 54e2d9552e930d..59d4bbc848a896 100644 --- a/arch/riscv/kernel/machine_kexec_file.c +++ b/arch/riscv/kernel/machine_kexec_file.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -266,7 +267,7 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start, kbuf.image = image; kbuf.buf_min = kernel_start + kernel_len; - kbuf.buf_max = ULONG_MAX; + kbuf.buf_max = PFN_PHYS(max_low_pfn); #ifdef CONFIG_CRASH_DUMP /* Add elfcorehdr */ From fedff1b52307413547d26826f366b7172db7bfaa Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Wed, 20 May 2026 18:00:00 +0800 Subject: [PATCH 084/521] UPSTREAM: riscv: dts: spacemit: enable PMIC on OrangePi R2S Enable the i2c8 interface and add the connected SpacemiT P1 PMIC and its associated regulators to support voltage regulation on the board. Signed-off-by: Chukun Pan Reviewed-by: Yixun lan Link: https://patch.msgid.link/20260520100000.575719-1-amadeus@jmu.edu.cn Signed-off-by: Yixun Lan (cherry picked from commit 793cc54475b49b5b558902b5c13e4bfe66530a50) Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-orangepi-r2s.dts | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts index 1ecc40749e5ac1..b13a8d6a2670e3 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts @@ -13,6 +13,7 @@ compatible = "xunlong,orangepi-r2s", "spacemit,k1"; aliases { + i2c8 = &i2c8; serial0 = &uart0; ethernet0 = ð0; ethernet1 = ð1; @@ -22,6 +23,15 @@ stdout-path = "serial0"; }; + vcc4v0: regulator-vcc4v0 { + compatible = "regulator-fixed"; + regulator-name = "vcc4v0"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <4000000>; + }; + vcc5v0_usb: regulator-vcc5v0-usb { compatible = "regulator-fixed"; enable-active-high; @@ -94,6 +104,130 @@ }; }; +&i2c8 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c8_cfg>; + status = "okay"; + + pmic@41 { + compatible = "spacemit,p1"; + reg = <0x41>; + interrupts = <64>; + vin1-supply = <&vcc4v0>; + vin2-supply = <&vcc4v0>; + vin3-supply = <&vcc4v0>; + vin4-supply = <&vcc4v0>; + vin5-supply = <&vcc4v0>; + vin6-supply = <&vcc4v0>; + aldoin-supply = <&vcc4v0>; + dldoin1-supply = <&buck5>; + dldoin2-supply = <&buck5>; + + regulators { + buck1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck3_1v8: buck3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck4_3v3: buck4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck5: buck5 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3450000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + aldo1_3v3: aldo1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + }; + + aldo2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + aldo3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + aldo4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + }; + + dldo2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo4 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-always-on; + }; + + dldo5 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + dldo6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + regulator-always-on; + }; + + dldo7 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + }; + }; +}; + &pdma { status = "okay"; }; From b1f99c56775c625ce77f7b2ce176e85a2096084d Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 26 May 2026 15:22:58 -0400 Subject: [PATCH 085/521] UPSTREAM: riscv: dts: spacemit: k3: Add Ziccrse extension for X100 cores Add the Ziccrse ISA extension to all eight X100 cores. Ziccrse provides a forward progress guarantee on LR/SC sequences in main memory regions with cacheability and coherence PMAs. The SpacemiT X100 core supports it per the SpacemiT K3 hardware specification. Signed-off-by: Guodong Xu Reviewed-by: Yixun lan Link: https://patch.msgid.link/20260526-k3-ziccrse-v1-1-c759792ca3a3@riscstar.com Signed-off-by: Yixun Lan (cherry picked from commit b4a789799d4c2be03a2c9a4e06f3ea817bb04416) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3.dtsi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 7e3cdd42ab8e3b..0b5c6c741ba14b 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -36,7 +36,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", @@ -77,7 +77,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", @@ -118,7 +118,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", @@ -159,7 +159,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", @@ -200,7 +200,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", @@ -241,7 +241,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", @@ -282,7 +282,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", @@ -323,7 +323,7 @@ "zawrs", "zba", "zbb", "zbc", "zbs", "zca", "zcb", "zcd", "zcmop", "zfa", "zfbfmin", "zfh", "zfhmin", "zicbom", "zicbop", "zicboz", - "ziccamoa", "ziccif", "zicclsm", "zicntr", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicond", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zimop", "zkt", "zvbb", "zvbc", "zvfbfmin", "zvfbfwma", "zvfh", From 084af6e0e630aece745b65d9e73b019e394e0996 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 28 May 2026 19:38:39 +0800 Subject: [PATCH 086/521] UPSTREAM: RISC-V: KVM: Enhance the logging check for mmu mapping When enabling dirty ring, the dirty bitmap is disable, and the logging check is always false as the RISC-V architecture does not select "NEED_KVM_DIRTY_RING_WITH_BITMAP". Although the dirty log is recorded since the write path already trying to add the dirty log, the logic for logging check is broken and some side effect will occurs. Enhance the logging check for mmu mapping so it can check both the dirty ring and the dirty bitmap. Signed-off-by: Inochi Amaoto Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260528113840.2629186-1-inochiama@gmail.com Signed-off-by: Anup Patel (cherry picked from commit 851334760330dafa68ab8f371e804ea6ad56ff9a) Signed-off-by: Han Gao --- arch/riscv/kvm/mmu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 2d3def024270c0..b5873bca9dcec6 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -142,9 +142,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, enum kvm_mr_change change) { /* - * At this point memslot has been committed and there is an - * allocated dirty_bitmap[], dirty pages will be tracked while - * the memory slot is write protected. + * At this point memslot has been committed and dirty pages will be + * tracked while the memory slot is write protected. */ if (change != KVM_MR_DELETE && new->flags & KVM_MEM_LOG_DIRTY_PAGES) { if (kvm_dirty_log_manual_protect_and_init_set(kvm)) @@ -433,8 +432,8 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, struct vm_area_struct *vma; struct kvm *kvm = vcpu->kvm; struct kvm_mmu_memory_cache *pcache = &vcpu->arch.mmu_page_cache; - bool logging = (memslot->dirty_bitmap && - !(memslot->flags & KVM_MEM_READONLY)) ? true : false; + bool logging = kvm_slot_dirty_track_enabled(memslot) && + !(memslot->flags & KVM_MEM_READONLY); unsigned long vma_pagesize, mmu_seq; struct kvm_gstage gstage; struct page *page; From f3940492feb4406d7ece56593c2433e006bb298d Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Tue, 23 Jun 2026 15:46:34 +0800 Subject: [PATCH 087/521] UPSTREAM: net: stmmac: dwmac-spacemit: Fix wrong phy interface definition The current MII interface register definition from the vendor is wrong, use the right number for the macro. Also, correct the interface mask in spacemit_set_phy_intf_sel() so it can update the register with the right number Fixes: 30f0ba420ed3 ("net: stmmac: Add glue layer for Spacemit K3 SoC") Signed-off-by: Inochi Amaoto Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260623074637.503864-2-inochiama@gmail.com Signed-off-by: Jakub Kicinski (cherry picked from commit d1e3a4c3b24d4430d9987a3f349c6c2a959a389a) Signed-off-by: Han Gao --- drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c index 223754cc5c79b7..3bfb6d49be6c0c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c @@ -18,8 +18,10 @@ #include "stmmac_platform.h" /* ctrl register bits */ -#define CTRL_PHY_INTF_RGMII BIT(3) -#define CTRL_PHY_INTF_MII BIT(4) +#define CTRL_PHY_INTF_MODE GENMASK(4, 3) +#define CTRL_PHY_INTF_RMII FIELD_PREP(CTRL_PHY_INTF_MODE, 0) +#define CTRL_PHY_INTF_RGMII FIELD_PREP(CTRL_PHY_INTF_MODE, 1) +#define CTRL_PHY_INTF_MII FIELD_PREP(CTRL_PHY_INTF_MODE, 3) #define CTRL_WAKE_IRQ_EN BIT(9) #define CTRL_PHY_IRQ_EN BIT(12) @@ -118,7 +120,7 @@ static void spacemit_get_interfaces(struct stmmac_priv *priv, void *bsp_priv, static int spacemit_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel) { - unsigned int mask = CTRL_PHY_INTF_MII | CTRL_PHY_INTF_RGMII; + unsigned int mask = CTRL_PHY_INTF_MODE; struct spacmit_dwmac *dwmac = bsp_priv; unsigned int val = 0; @@ -128,6 +130,7 @@ static int spacemit_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel) break; case PHY_INTF_SEL_RMII: + val = CTRL_PHY_INTF_RMII; break; case PHY_INTF_SEL_RGMII: From ac84c711d0204fb8a31a3b8ccebbcdaa2f2046f1 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Tue, 23 Jun 2026 15:46:35 +0800 Subject: [PATCH 088/521] UPSTREAM: net: stmmac: dwmac-spacemit: Fix wrong irq definition The current irq definition of the wake irq and the lpi irq is wrong, replace them with the right number and name. Fixes: 30f0ba420ed3 ("net: stmmac: Add glue layer for Spacemit K3 SoC") Signed-off-by: Inochi Amaoto Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260623074637.503864-3-inochiama@gmail.com Signed-off-by: Jakub Kicinski (cherry picked from commit bf5cd5d4ca423d348e01b3bbcdccd0e02b1b408b) Signed-off-by: Han Gao --- drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c index 3bfb6d49be6c0c..322bdf167a4a68 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c @@ -22,8 +22,8 @@ #define CTRL_PHY_INTF_RMII FIELD_PREP(CTRL_PHY_INTF_MODE, 0) #define CTRL_PHY_INTF_RGMII FIELD_PREP(CTRL_PHY_INTF_MODE, 1) #define CTRL_PHY_INTF_MII FIELD_PREP(CTRL_PHY_INTF_MODE, 3) -#define CTRL_WAKE_IRQ_EN BIT(9) -#define CTRL_PHY_IRQ_EN BIT(12) +#define CTRL_LPI_IRQ_EN BIT(9) +#define CTRL_WAKE_IRQ_EN BIT(12) /* dline register bits */ #define RGMII_RX_DLINE_EN BIT(0) From 0b6c819707b115024b5bf1a9c314568e87163d39 Mon Sep 17 00:00:00 2001 From: Sandie Cao Date: Tue, 21 Apr 2026 14:42:00 +0800 Subject: [PATCH 089/521] UPSTREAM: dt-bindings: riscv: spacemit: add deepcomputing,fml13v05 Document the compatible string for the Deepcomputing fml13v05. It's based on the SpacemiT K3 RISC-V SoC and is designed for the Framework Laptop 13 Chassis, which has (Framework) SKU FRANHQ0001. Acked-by: Conor Dooley Signed-off-by: Sandie Cao Reviewed-by: Troy Mitchell Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260421064200.1582367-1-sandie.cao@deepcomputing.io Signed-off-by: Yixun Lan (cherry picked from commit a17be027dc5ccd51b99b5bc7b487ba9fbdc554e8) Signed-off-by: Han Gao --- Documentation/devicetree/bindings/riscv/spacemit.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/riscv/spacemit.yaml b/Documentation/devicetree/bindings/riscv/spacemit.yaml index b958b94a924dee..af8030242bdc8c 100644 --- a/Documentation/devicetree/bindings/riscv/spacemit.yaml +++ b/Documentation/devicetree/bindings/riscv/spacemit.yaml @@ -29,6 +29,7 @@ properties: - const: spacemit,k1 - items: - enum: + - deepcomputing,fml13v05 - spacemit,k3-pico-itx - const: spacemit,k3 From 3640fc7ecc6c7ac795224eaa176b59a0ca6b6858 Mon Sep 17 00:00:00 2001 From: Sandie Cao Date: Tue, 21 Apr 2026 14:43:06 +0800 Subject: [PATCH 090/521] UPSTREAM: riscv: dts: spacemit: add DeepComputing FML13V05 board device tree The FML13V05 board from DeepComputing incorporates a SpacemiT K3 RISC-V SoC.It is a mainboard designed for the Framework Laptop 13 Chassis, which has (Framework) SKU FRANHQ0001. The FML13V05 board features: - SpacemiT K3 RISC-V SoC - LPDDR5 16GB or 32GB - eMMC 32GB ~128GB (Optional) - UFS 3.1 256G (Optional) - QSPI Flash - MicroSD Slot - PCIe-based Wi-Fi - 4 USB-C Ports - Port 1: PD 3.0 (65W Max), USB 3.2 Gen 1 - Port 2: PD 3.0 (65W Max), USB 3.2 Gen 1, DP 1.4 (4K@60Hz) - Port 3 & 4: USB 3.2 Gen 1 This minimal device tree enables booting into a serial console with UART output. Signed-off-by: Sandie Cao Reviewed-by: Troy Mitchell Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260421064306.1583888-1-sandie.cao@deepcomputing.io Signed-off-by: Yixun Lan (cherry picked from commit 448fedd56cdfa68c2e661b16a8dfc8f3c40a1bcf) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/Makefile | 1 + .../spacemit/k3-deepcomputing-fml13v05.dts | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 arch/riscv/boot/dts/spacemit/k3-deepcomputing-fml13v05.dts diff --git a/arch/riscv/boot/dts/spacemit/Makefile b/arch/riscv/boot/dts/spacemit/Makefile index 7e2b8770257181..acb993c452ba22 100644 --- a/arch/riscv/boot/dts/spacemit/Makefile +++ b/arch/riscv/boot/dts/spacemit/Makefile @@ -4,4 +4,5 @@ dtb-$(CONFIG_ARCH_SPACEMIT) += k1-milkv-jupiter.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k1-musepi-pro.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k1-orangepi-r2s.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k1-orangepi-rv2.dtb +dtb-$(CONFIG_ARCH_SPACEMIT) += k3-deepcomputing-fml13v05.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k3-pico-itx.dtb diff --git a/arch/riscv/boot/dts/spacemit/k3-deepcomputing-fml13v05.dts b/arch/riscv/boot/dts/spacemit/k3-deepcomputing-fml13v05.dts new file mode 100644 index 00000000000000..b5d5112de35813 --- /dev/null +++ b/arch/riscv/boot/dts/spacemit/k3-deepcomputing-fml13v05.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2026 DeepComputing (HK) Limited + */ + +#include "k3.dtsi" +#include "k3-pinctrl.dtsi" + +/ { + model = "DeepComputing FML13V05"; + compatible = "deepcomputing,fml13v05", "spacemit,k3"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0"; + }; + + memory@100000000 { + device_type = "memory"; + reg = <0x1 0x00000000 0x4 0x00000000>; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_0_cfg>; + status = "okay"; +}; From 241a7ddaf3b0def66310305f1d1ea8d25ee153ad Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Wed, 20 May 2026 23:45:27 +0000 Subject: [PATCH 091/521] UPSTREAM: dt-bindings: riscv: spacemit: Add K3 CoM260-IFX board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SpacemiT K3 CoM260-IFX board combines a 69.6 × 45 mm compute module with a reference carrier board. The module integrates up to 32GB LPDDR5 memory, UFS storage, Micro SD card slot and includes interfaces such as dual MIPI CSI-2 connectors, M.2 expansion, USB 3.0, Gigabit Ethernet, DisplayPort, and a 40-pin expansion header. The carrier board is intended as a general-purpose development platform for CoM260 module and exposes interfaces for all of storage, display, networking, and camera connectivity. Acked-by: Conor Dooley Link: https://patch.msgid.link/20260520-02-k3-com260-ifx-v2-1-d55095457cf0@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit 3abf3f5c84fd32a95132a403e0209c8010b227f8) Signed-off-by: Han Gao --- Documentation/devicetree/bindings/riscv/spacemit.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/riscv/spacemit.yaml b/Documentation/devicetree/bindings/riscv/spacemit.yaml index af8030242bdc8c..311bbccba0baa2 100644 --- a/Documentation/devicetree/bindings/riscv/spacemit.yaml +++ b/Documentation/devicetree/bindings/riscv/spacemit.yaml @@ -32,6 +32,11 @@ properties: - deepcomputing,fml13v05 - spacemit,k3-pico-itx - const: spacemit,k3 + - items: + - enum: + - spacemit,k3-com260-ifx + - const: spacemit,k3-com260 + - const: spacemit,k3 additionalProperties: true From 6fa5db35a44ef4ec29b82bc9946845bd1cce3e89 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Wed, 20 May 2026 23:45:28 +0000 Subject: [PATCH 092/521] UPSTREAM: riscv: dts: spacemit: k3: Initial support for CoM260-IFX board The K3 CoM260-IFX board combine with one 260 pins "Gold Finger" computer module with a carrier board. The module integrates the K3 SoC, LPDDR5, UFS storage, Gigabit Ethernet, Micro SD card, PMIC Chip. The board offers a comprehensive array of interfaces, including MIPI-DSI, MIPI-CSI, DisplayPort, SDIO, SPI, I2S, I2C, CAN-FD, PWM, UART, USB, PCIe, and GMAC. Add initial support for enabling Serial UART and ethernet. Link: https://patch.msgid.link/20260520-02-k3-com260-ifx-v2-2-d55095457cf0@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit cfe5c91cb73c87f9021e446ec9d323c483851c46) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/Makefile | 1 + .../riscv/boot/dts/spacemit/k3-com260-ifx.dts | 21 ++ arch/riscv/boot/dts/spacemit/k3-com260.dtsi | 190 ++++++++++++++++++ arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 33 +++ 4 files changed, 245 insertions(+) create mode 100644 arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts create mode 100644 arch/riscv/boot/dts/spacemit/k3-com260.dtsi diff --git a/arch/riscv/boot/dts/spacemit/Makefile b/arch/riscv/boot/dts/spacemit/Makefile index acb993c452ba22..28ec9e1e0a625c 100644 --- a/arch/riscv/boot/dts/spacemit/Makefile +++ b/arch/riscv/boot/dts/spacemit/Makefile @@ -4,5 +4,6 @@ dtb-$(CONFIG_ARCH_SPACEMIT) += k1-milkv-jupiter.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k1-musepi-pro.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k1-orangepi-r2s.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k1-orangepi-rv2.dtb +dtb-$(CONFIG_ARCH_SPACEMIT) += k3-com260-ifx.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k3-deepcomputing-fml13v05.dtb dtb-$(CONFIG_ARCH_SPACEMIT) += k3-pico-itx.dtb diff --git a/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts b/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts new file mode 100644 index 00000000000000..238bb03d0e9e23 --- /dev/null +++ b/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2026 SpacemiT (Hangzhou) Technology Co. Ltd + * Copyright (c) 2026 Yixun Lan + */ + +#include "k3-com260.dtsi" + +/ { + model = "SpacemiT K3 CoM260 IFX"; + compatible = "spacemit,k3-com260-ifx", "spacemit,k3-com260", "spacemit,k3"; + + aliases { + serial0 = &uart0; + ethernet0 = ð1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3-com260.dtsi b/arch/riscv/boot/dts/spacemit/k3-com260.dtsi new file mode 100644 index 00000000000000..a38d7b738258de --- /dev/null +++ b/arch/riscv/boot/dts/spacemit/k3-com260.dtsi @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2026 SpacemiT (Hangzhou) Technology Co. Ltd + * Copyright (c) 2026 Yixun Lan + */ +#include + +#include "k3.dtsi" +#include "k3-pinctrl.dtsi" + +/ { + model = "SpacemiT K3 CoM260 Module"; + compatible = "spacemit,k3-com260", "spacemit,k3"; + + memory@100000000 { + device_type = "memory"; + reg = <0x1 0x00000000 0x4 0x00000000>; + }; + + reg_5v_sys: regulator-5v-sys { + compatible = "regulator-fixed"; + regulator-name = "P5V0_SYS"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; +}; + +&i2c8 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c8_cfg>; + status = "okay"; + + pmic@41 { + compatible = "spacemit,p1"; + reg = <0x41>; + interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; + vin1-supply = <®_5v_sys>; + vin2-supply = <®_5v_sys>; + vin3-supply = <®_5v_sys>; + vin4-supply = <®_5v_sys>; + vin5-supply = <®_5v_sys>; + vin6-supply = <®_5v_sys>; + aldoin-supply = <®_5v_sys>; + dldoin1-supply = <&buck4>; + dldoin2-supply = <&buck4>; + + regulators { + buck1: buck1 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck2: buck2 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck3: buck3 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck4: buck4 { + regulator-min-microvolt = <2100000>; + regulator-max-microvolt = <2100000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck5: buck5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck6: buck6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <500000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + aldo1: aldo1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + aldo2: aldo2 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + aldo3: aldo3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + aldo4: aldo4 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo1: dldo1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo2: dldo2 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo3: dldo3 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo4: dldo4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + + dldo5: dldo5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo6: dldo6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo7: dldo7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; +}; + +ð1 { + pinctrl-names = "default"; + pinctrl-0 = <&gmac1_rgmii_0_cfg>, <&gmac1_phy_0_cfg>; + phy-mode = "rgmii-id"; + phy-handle = <&phy1>; + status = "okay"; + + mdio { + phy1: phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + reset-gpios = <&gpio 1 5 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <10000>; + }; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_0_cfg>; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi index 252c64af76fe19..3ee1471f37985f 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -45,6 +45,39 @@ }; }; + gmac1_rgmii_0_cfg: gmac1-rgmii-0-cfg { + gmac1-rgmii-0-pins { + pinmux = , /* gmac1_rxdv */ + , /* gmac1 rx d0 */ + , /* gmac1 rx d1 */ + , /* gmac1 rx_clk */ + , /* gmac1 rx d2 */ + , /* gmac1 rx d3 */ + , /* gmac1 tx d0 */ + , /* gmac1 tx d1 */ + , /* gmac1 tx clk */ + , /* gmac1 tx d2 */ + , /* gmac1 tx d3 */ + , /* gmac1 tx_en */ + , /* gmac1 mdc */ + ; /* gmac1 mdio */ + + bias-disable; + drive-strength = <25>; + power-source = <1800>; + }; + }; + + gmac1_phy_0_cfg: gmac1-phy-0-cfg { + gmac1-phy-0-pins { + pinmux = ; /* gmac1 int */ + + bias-disable; + drive-strength = <25>; + power-source = <1800>; + }; + }; + /omit-if-no-ref/ i2c8_cfg: i2c8-cfg { i2c8-pins { From 9ec0ca5c9130b19013b572f16042a833e1de5ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 093/521] UPSTREAM: riscv: Implement ARCH_HAS_CC_CAN_LINK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic CC_CAN_LINK detection relies on -m32/-m64 compiler flags. These are not supported by riscv compilers. Use architecture-specific logic using -mabi instead. Prefer the 'd' ABI variant when possible as todays toolchains are most likely to provide a libc for that one. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260316-cc-can-link-riscv-v4-1-64c072b456dd@linutronix.de Signed-off-by: Paul Walmsley (cherry picked from commit 84894ceb3c2ef5c5404359efd4edc6c438aa6d0c) Signed-off-by: Han Gao --- arch/riscv/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index f54001f9c328ce..390ae76c31ec29 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -24,6 +24,7 @@ config RISCV select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_BINFMT_FLAT + select ARCH_HAS_CC_CAN_LINK select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_VM_PGTABLE @@ -1354,6 +1355,20 @@ config PORTABLE config ARCH_PROC_KCORE_TEXT def_bool y +config ARCH_CC_CAN_LINK + bool + default $(cc_can_link_user,-march=rv64g -mabi=lp64d) if 64BIT && FPU + default $(cc_can_link_user,-march=rv64g -mabi=lp64) if 64BIT + default $(cc_can_link_user,-march=rv32g -mabi=ilp32d) if FPU + default $(cc_can_link_user,-march=rv32g -mabi=ilp32) + +config ARCH_USERFLAGS + string + default "-march=rv64g -mabi=lp64d" if 64BIT && FPU + default "-march=rv64g -mabi=lp64" if 64BIT + default "-march=rv32g -mabi=ilp32d" if FPU + default "-march=rv32g -mabi=ilp32" + menu "Power management options" source "kernel/power/Kconfig" From 563bd558615237efd8c67abe212b02ccdb02ddde Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 30 Mar 2026 22:15:21 +0000 Subject: [PATCH 094/521] UPSTREAM: riscv: dts: spacemit: k3: Add USB2.0 support There is one USB2.0 host in K3 SoC which use DWC3 IP but only provide USB2.0 functionality, and with only one USB2 PHY connected. The USB2.0 controller on Pico-ITX board connects to a Terminus FE1.1 Hub which fully USB2.0 protocol compliant and provides 4 ports. Link: https://patch.msgid.link/20260330-02-k3-usb20-dts-v2-1-46af262fb4a9@kernel.org Signed-off-by: Yixun Lan (cherry picked from commit d3bd58f3060f28ecd30df51925a9b1f62e5e8c9b) Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 24 +++++++++++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 31 ++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index 4486dc1fe114da..b89c1521e6649b 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -26,6 +26,14 @@ reg = <0x1 0x00000000 0x4 0x00000000>; }; + reg_aux_vcc3v3: regulator-aux-vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "AUX_VCC3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + reg_aux_vcc5v: regulator-aux-vcc5v { compatible = "regulator-fixed"; regulator-name = "AUX_VCC5V"; @@ -197,3 +205,19 @@ pinctrl-0 = <&uart0_0_cfg>; status = "okay"; }; + +&usb2_host { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + hub@1 { + compatible = "usb1a40,0101"; + reg = <1>; + vdd-supply = <®_aux_vcc3v3>; + }; +}; + +&usb2_phy { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 0b5c6c741ba14b..19fc9b49668edb 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -438,6 +438,37 @@ dma-noncoherent; ranges; + usb2_host: usb@c0a00000 { + compatible = "spacemit,k3-dwc3"; + reg = <0x0 0xc0a00000 0x0 0x10000>; + clocks = <&syscon_apmu CLK_APMU_USB2_BUS>; + clock-names = "usbdrd30"; + resets = <&syscon_apmu RESET_APMU_USB2_AHB>, + <&syscon_apmu RESET_APMU_USB2_VCC>, + <&syscon_apmu RESET_APMU_USB2_PHY>; + reset-names = "ahb", "vcc", "phy"; + interrupts = <105 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; + phy_type = "utmi"; + snps,dis_enblslpm_quirk; + snps,dis_u2_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; + dr_mode = "host"; + maximum-speed = "high-speed"; + status = "disabled"; + }; + + usb2_phy: phy@c0a20000 { + compatible = "spacemit,k3-usb2-phy"; + reg = <0x0 0xc0a20000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_USB2_BUS>; + #phy-cells = <0>; + status = "disabled"; + }; + eth0: ethernet@cac80000 { compatible = "spacemit,k3-dwmac", "snps,dwmac-5.40a"; reg = <0x0 0xcac80000 0x0 0x2000>; From 60eeea2c6b4bbd2aa5f5b8c1dc0a3a54f2a5ac89 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Tue, 26 May 2026 16:36:57 +0200 Subject: [PATCH 095/521] UPSTREAM: i2c: busses: make K1 driver default for SpacemiT platforms Enable I2C_K1 by default when ARCH_SPACEMIT is configured to ensure SD card functionality works out-of-the-box. SpacemiT K1 boards use I2C-controlled PMICs (like the P1 chip) to provide SD card power supplies. Without the I2C_K1 driver enabled, regulators cannot be controlled and SD card detection/operation fails. Suggested-by: Margherita Milani Suggested-by: Yixun Lan Signed-off-by: Iker Pedrosa Reviewed-by: Yixun Lan Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260526-orangepi-sd-card-i2c-v1-1-b92268bfd467@gmail.com (cherry picked from commit d38e710fba1806974051972d69fbbd6c69b55734) Signed-off-by: Han Gao --- drivers/i2c/busses/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8c935f867a37a7..bfe191e50b49a2 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -794,6 +794,7 @@ config I2C_K1 tristate "SpacemiT K1 I2C adapter" depends on ARCH_SPACEMIT || COMPILE_TEST depends on OF + default ARCH_SPACEMIT help This option enables support for the I2C interface on the SpacemiT K1 platform. From 036d8971375fd6414f1a135c80746f16f970f9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:50 +0200 Subject: [PATCH 096/521] UPSTREAM: riscv: vdso: Always declare vdso_start symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the declarations of vdso_start and its related symbols always visible. With that their users don't have to use ifdeffery but can use the better IS_ENABLED() compile-time checks. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-1-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley (cherry picked from commit 499578e22ae0cc33d05803f22e5c71ffb7077b2f) Signed-off-by: Han Gao --- arch/riscv/include/asm/vdso.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/riscv/include/asm/vdso.h b/arch/riscv/include/asm/vdso.h index 35bf830a557612..f7998d9ad9b2d7 100644 --- a/arch/riscv/include/asm/vdso.h +++ b/arch/riscv/include/asm/vdso.h @@ -12,12 +12,15 @@ * All systems with an MMU have a VDSO, but systems without an MMU don't * support shared libraries and therefore don't have one. */ -#ifdef CONFIG_MMU #define __VDSO_PAGES 4 #ifndef __ASSEMBLER__ + +#ifdef CONFIG_MMU #include +#endif + #ifdef CONFIG_RISCV_USER_CFI #include #endif @@ -38,15 +41,12 @@ #define COMPAT_VDSO_SYMBOL(base, name) \ (void __user *)((unsigned long)(base) + compat__vdso_##name##_offset) -extern char compat_vdso_start[], compat_vdso_end[]; - #endif /* CONFIG_COMPAT */ extern char vdso_start[], vdso_end[]; extern char vdso_cfi_start[], vdso_cfi_end[]; +extern char compat_vdso_start[], compat_vdso_end[]; #endif /* !__ASSEMBLER__ */ -#endif /* CONFIG_MMU */ - #endif /* _ASM_RISCV_VDSO_H */ From dd06adb8aa5f448d51bb1ca6623e1ecf04dc970a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:51 +0200 Subject: [PATCH 097/521] UPSTREAM: riscv: alternative: Use IS_ENABLED() over ifdeffery for apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IS_ENABLED() allows better compilation coverage while still optimizing away all the dead code. Also it will make some upcoming changes easier. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-2-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley (cherry picked from commit 4fd6505f189d447abbed1f1f6fe6b82649f27a46) Signed-off-by: Han Gao --- arch/riscv/kernel/alternative.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 7642704c7f1841..59991922a5dca5 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,7 +173,6 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -#ifdef CONFIG_MMU static void __init apply_vdso_alternatives(void) { const Elf_Ehdr *hdr; @@ -194,9 +193,6 @@ static void __init apply_vdso_alternatives(void) (struct alt_entry *)end, RISCV_ALTERNATIVES_BOOT); } -#else -static void __init apply_vdso_alternatives(void) { } -#endif void __init apply_boot_alternatives(void) { @@ -207,7 +203,8 @@ void __init apply_boot_alternatives(void) (struct alt_entry *)__alt_end, RISCV_ALTERNATIVES_BOOT); - apply_vdso_alternatives(); + if (IS_ENABLED(CONFIG_MMU)) + apply_vdso_alternatives(); } /* From 8b048163583a248e5f7016fcc47f386b7131b63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:52 +0200 Subject: [PATCH 098/521] UPSTREAM: riscv: alternative: Pass vDSO start as parameter to apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dedicated vDSO with CFI should also be patched in the same way. To prepare for that move the currently hardcoded vDSO start symbol into a parameter. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-3-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley (cherry picked from commit 6386161abb02880ace2cc965e73f7857b351706c) Signed-off-by: Han Gao --- arch/riscv/kernel/alternative.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 59991922a5dca5..89c283a5cec7ff 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,14 +173,14 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -static void __init apply_vdso_alternatives(void) +static void __init apply_vdso_alternatives(void *start) { const Elf_Ehdr *hdr; const Elf_Shdr *shdr; const Elf_Shdr *alt; struct alt_entry *begin, *end; - hdr = (Elf_Ehdr *)vdso_start; + hdr = (Elf_Ehdr *)start; shdr = (void *)hdr + hdr->e_shoff; alt = find_section(hdr, shdr, ".alternative"); if (!alt) @@ -204,7 +204,7 @@ void __init apply_boot_alternatives(void) RISCV_ALTERNATIVES_BOOT); if (IS_ENABLED(CONFIG_MMU)) - apply_vdso_alternatives(); + apply_vdso_alternatives(vdso_start); } /* From 4e8422dc31bdfce8949a2e82354aafef914077d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:53 +0200 Subject: [PATCH 099/521] UPSTREAM: riscv: alternative: Also patch the CFI vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dedicated vDSO for CFI-enabled userspace can also contain alternative entries. Patch those, too. Fixes: ccad8c1336b6 ("arch/riscv: add dual vdso creation logic and select vdso based on hw") Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-4-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley (cherry picked from commit d3e0634787a234b40a740b9c398fd320a68db81c) Signed-off-by: Han Gao --- arch/riscv/kernel/alternative.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 89c283a5cec7ff..104dc0862c5c2e 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -205,6 +205,9 @@ void __init apply_boot_alternatives(void) if (IS_ENABLED(CONFIG_MMU)) apply_vdso_alternatives(vdso_start); + + if (IS_ENABLED(CONFIG_RISCV_USER_CFI)) + apply_vdso_alternatives(vdso_cfi_start); } /* From 3ad8323c5ec08297671dbbbba72a17d6bab492bc Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Thu, 18 Jun 2026 18:12:30 +0200 Subject: [PATCH 100/521] UPSTREAM: riscv: Add build salt to the vDSO The vDSO needs to have a unique build id in a similar manner to the kernel and modules. Use the build salt macro. Signed-off-by: Bastian Blank Reviewed-by: Nam Cao Link: https://patch.msgid.link/ajQY7n0an0YwQ--j@steamhammer.waldi.eu.org Signed-off-by: Paul Walmsley (cherry picked from commit c8c5a7835f5c9e34c8a15190519a2cc9ecb9b5b5) Signed-off-by: Han Gao --- arch/riscv/kernel/vdso/note.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/vdso/note.S b/arch/riscv/kernel/vdso/note.S index 3d92cc956b95b9..69bfe48be037a2 100644 --- a/arch/riscv/kernel/vdso/note.S +++ b/arch/riscv/kernel/vdso/note.S @@ -4,6 +4,7 @@ * Here we can supply some information useful to userland. */ +#include #include #include #include @@ -12,4 +13,6 @@ ELFNOTE_START(Linux, 0, "a") .long LINUX_VERSION_CODE ELFNOTE_END +BUILD_SALT + emit_riscv_feature_1_and From 71c88493f98edc4900522f248b71d781a52038d0 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 30 Jun 2026 21:40:03 +0200 Subject: [PATCH 101/521] UPSTREAM: riscv: probes: save original sp in rethook trampoline Reading a word from the stack in a kretprobe crashes a risc-v kernel. $ cd /sys/kernel/tracing/ $ echo 'r n_tty_write $stack0' > dynamic_events $ echo 1 > events/kprobes/enable Unable to handle kernel paging request at virtual address 0000000200000128 ... [] regs_get_kernel_stack_nth+0x26/0x38 [] process_fetch_insn+0x3ee/0x760 [] kretprobe_trace_func+0x116/0x1f0 [] kretprobe_dispatcher+0x4a/0x58 [] kretprobe_rethook_handler+0x5e/0x90 [] rethook_trampoline_handler+0x70/0x108 [] arch_rethook_trampoline_callback+0x12/0x1c [] arch_rethook_trampoline+0x48/0x94 [] tty_write+0x1a/0x30 In regs_get_kernel_stack_nth, regs->sp contains an arbitrary value. arch_rethook_trampoline saves the registers from the probed function in a struct pt_regs. sp is not saved. Instead, sp is decremented for arch_rethook_trampoline's local stack. Fix this crash and save the original sp along with the other registers. Use a0 as a temporary register, it is overwritten anyway. Cc: stable@vger.kernel.org Fixes: c22b0bcb1dd02 ("riscv: Add kprobes supported") Signed-off-by: Martin Kaiser Acked-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260630194010.1824039-1-martin@kaiser.cx [pjw@kernel.org: added Fixes tag; cc'ed stable] Signed-off-by: Paul Walmsley (cherry picked from commit bc7b086a45521a986a49045907f017e3e46c763e) Signed-off-by: Han Gao --- arch/riscv/kernel/probes/rethook_trampoline.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/probes/rethook_trampoline.S b/arch/riscv/kernel/probes/rethook_trampoline.S index f2cd83d9b0f004..c3aa8d8cf5aff1 100644 --- a/arch/riscv/kernel/probes/rethook_trampoline.S +++ b/arch/riscv/kernel/probes/rethook_trampoline.S @@ -41,6 +41,9 @@ REG_S x29, PT_T4(sp) REG_S x30, PT_T5(sp) REG_S x31, PT_T6(sp) + /* save original sp */ + addi a0, sp, PT_SIZE_ON_STACK + REG_S a0, PT_SP(sp) .endm .macro restore_all_base_regs From 199ba6176cd8eebcd76b304ba207c23cf884d8c9 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 25 Jun 2026 17:47:02 +0800 Subject: [PATCH 102/521] UPSTREAM: ACPI: RIMT: Only defer the IOMMU configuration in init stage The IOMMU configuration will be deferred if the IOMMU driver isn't probed by the time. Make this deferral only in the initialization stage with driver_deferred_probe_check_state(). Otherwise the devices depends on IOMMU will be deferred forever in case the IOMMU device probe failed or it doesn't appear in the ACPI namespace. Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT") Signed-off-by: Yicong Yang Link: https://patch.msgid.link/20260625094702.11558-1-yang.yicong@picoheart.com [pjw@kernel.org: added Fixes line] Signed-off-by: Paul Walmsley (cherry picked from commit b8f62414fa05144924257db283c5c35f74d21121) Signed-off-by: Han Gao --- drivers/acpi/riscv/rimt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c index 906282b0e63c0c..e4538fa6c2c822 100644 --- a/drivers/acpi/riscv/rimt.c +++ b/drivers/acpi/riscv/rimt.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -257,11 +258,11 @@ static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 rimt_fwnode = rimt_get_fwnode(node); /* - * The IOMMU drivers may not be probed yet. - * Defer the IOMMU configuration + * The IOMMU drivers may not be probed yet. Defer the IOMMU + * configuration if it's still in initialization stage. */ if (!rimt_fwnode) - return -EPROBE_DEFER; + return driver_deferred_probe_check_state(dev); /* * EPROBE_DEFER ensures IOMMU is probed before the devices that From d2bd8b9829739b26839bb27be3fec04377f9524f Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Mon, 6 Jul 2026 11:27:07 +1200 Subject: [PATCH 103/521] UPSTREAM: riscv: Prevent NULL pointer dereference in machine_kexec_prepare() A NULL pointer dereference issue is noticed in riscv's machine_kexec_prepare(), where image->segment[i].buf might be NULL and copied unchecked. The NULL buf comes from ima_add_kexec_buffer(), where kbuf is added by kexec_add_buffer(), but kbuf.buffer is NULL, then it is copied without a check in machine_kexec_prepare(): kexec_file_load -> kimage_file_alloc_init() -> kimage_file_prepare_segments() -> ima_add_kexec_buffer() -> kexec_add_buffer() -> machine_kexec_prepare() -> memcpy() Address this by adding a check before the data copy attempt. Fixes: b7fb4d78a6ad ("RISC-V: use memcpy for kexec_file mode") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/kexec/CAO7dBbVftLUhd2qrh7hmijTB3PEPfZAhykCGqEfrPoOcSrrj-w@mail.gmail.com/ Acked-by: Baoquan He Acked-by: Pratyush Yadav Reviewed-by: Nutty Liu Signed-off-by: Tao Liu Link: https://patch.msgid.link/20260705232706.30265-2-ltao@redhat.com Signed-off-by: Paul Walmsley (cherry picked from commit 81bbcff0c053c4f5c711c31a9b72fc492bd96c3f) Signed-off-by: Han Gao --- arch/riscv/kernel/machine_kexec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c index 2306ce3e5f229f..738df176ff6f16 100644 --- a/arch/riscv/kernel/machine_kexec.c +++ b/arch/riscv/kernel/machine_kexec.c @@ -41,6 +41,9 @@ machine_kexec_prepare(struct kimage *image) if (image->segment[i].memsz <= sizeof(fdt)) continue; + if (!image->segment[i].buf) + continue; + if (image->file_mode) memcpy(&fdt, image->segment[i].buf, sizeof(fdt)); else if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt))) From 247abe59c0b4057c18b771d652e64c57ae733664 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Tue, 7 Jul 2026 16:23:48 +0800 Subject: [PATCH 104/521] UPSTREAM: selftests/rseq: Fix a building error for riscv arch RISC-V rseq selftests include asm/fence.h from tools/arch/riscv, but the rseq Makefile only adds tools/include in the CFLAGS, this results in the building failure both for native and cross build: In file included from rseq.h:131, from rseq.c:37: rseq-riscv.h:11:10: fatal error: asm/fence.h: No such file or directory To fix it, add the matching tools/arch/$(ARCH)/include path in the CFLAGS and derive ARCH from SUBARCH for standalone native builds where ARCH is not set. Fixes: c92786e179e0 ("KVM: riscv: selftests: Use the existing RISCV_FENCE macro in `rseq-riscv.h`") Cc: stable@vger.kernel.org Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260707082348.36896-1-hui.wang@canonical.com Signed-off-by: Paul Walmsley (cherry picked from commit a2ac823d8a228e392bc6a79ea63b550ab84a55b2) Signed-off-by: Han Gao --- tools/testing/selftests/rseq/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile index 50d69e22ee7a67..aba6317f6cb8e5 100644 --- a/tools/testing/selftests/rseq/Makefile +++ b/tools/testing/selftests/rseq/Makefile @@ -5,9 +5,13 @@ CLANG_FLAGS += -no-integrated-as endif top_srcdir = ../../../.. +include $(top_srcdir)/scripts/subarch.include +ARCH ?= $(SUBARCH) +LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \ - $(CLANG_FLAGS) -I$(top_srcdir)/tools/include + $(CLANG_FLAGS) -I$(top_srcdir)/tools/include \ + -I$(LINUX_TOOL_ARCH_INCLUDE) LDLIBS += -lpthread -ldl # Own dependencies because we only want to build against 1st prerequisite, but From d224ac8550ab3b0a85843037c78030dbfa5c38a5 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 02:58:37 +0000 Subject: [PATCH 105/521] UPSTREAM: reset: spacemit: k3: fix USB2 ahb reset According to SpacemiT K3's updated docs, the USB2 ahb reset and USB2 bus clock enable bit was wrongly swapped, the correct one should be: Register : APMU_USB_CLK_RES_CTRL bit[1] : usb2_port_bus_clk_en bit[0] : usb2_port_ahb_rstn Fixes: a0e0c2f8c5f3 ("reset: spacemit: k3: Decouple composite reset lines") Reported-by: Junzhong Pan Signed-off-by: Yixun Lan Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel (cherry picked from commit 1ca22c6aa006b05143367268066fb74e32cfe66b) Signed-off-by: Han Gao --- drivers/reset/spacemit/reset-spacemit-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c index 9841f5e057b2a0..2e87f320cf1184 100644 --- a/drivers/reset/spacemit/reset-spacemit-k3.c +++ b/drivers/reset/spacemit/reset-spacemit-k3.c @@ -112,7 +112,7 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)), - [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(0)), [RESET_APMU_USB2_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(2)), [RESET_APMU_USB2_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(3)), [RESET_APMU_USB3_A_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(5)), From dafad915168f8e236bd9c6e30c4e61abf48c2d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 1 Jul 2026 11:21:22 +0200 Subject: [PATCH 106/521] UPSTREAM: riscv: vdso: Do not use LTO for the vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With LTO enabled the compiler assumes that the vDSO functions are not used and optimizes them away completely. Currently this happens to __vdso_clock_getres(), __vdso_clock_gettime(), __vdso_getrandom(), __vdso_gettimeofday() and __vdso_riscv_hwprobe(). Disable LTO for the vDSO, as these functions are hand-optimized anyways. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606301855.WvkSC4kD-lkp@intel.com/ Fixes: 021d23428bdb ("RISC-V: build: Allow LTO to be selected") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260701-riscv-vdso-lto-v1-1-89db0cd82077@linutronix.de Signed-off-by: Paul Walmsley (cherry picked from commit ad6dcfa023762e37962f77ee48e752b7570e9440) Signed-off-by: Han Gao --- arch/riscv/kernel/vdso/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index a842dc034571da..43ee881f6c6fd1 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -69,9 +69,9 @@ CPPFLAGS_$(vdso_lds) += -DHAS_VGETTIMEOFDAY endif # Disable -pg to prevent insert call site -CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) +CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) # Force dependency $(obj)/$(vdso_o): $(obj)/$(vdso_so) From 1dd01314fa01f6d9c2806d82658c05cf719a2f93 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Mon, 13 Jul 2026 11:29:52 -0600 Subject: [PATCH 107/521] UPSTREAM: riscv: mm: Make mark_new_valid_map() stuff depend on 64BIT && MMU None of the code relating to mark_new_valid_map() does anything useful without CONFIG_64BIT=y && CONFIG_MMU=y, because the new_valid_map_cpus_check code is only used if CONFIG_64BIT, and the exception codes checked there can only happen with CONFIG_MMU=y. Therefore, make these conditional on CONFIG_64BIT=y && CONFIG_MMU=y to simplify programming, since we do not have to handle CONFIG_MMU=n when changing this code in the future. This also removes some unused code on the entry path for CONFIG_MMU=n. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260713-mark-after-vmemmap-populate-v6-1-b945ceba29d4@iscas.ac.cn Signed-off-by: Paul Walmsley (cherry picked from commit b6ea9680f8c101967caf9981c2980b80b818ccbf) Signed-off-by: Han Gao --- arch/riscv/include/asm/cacheflush.h | 2 +- arch/riscv/kernel/entry.S | 2 +- arch/riscv/mm/init.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 8cfe59483a8f28..58e787fad029fd 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -40,7 +40,7 @@ do { \ flush_icache_mm(vma->vm_mm, 0); \ } while (0) -#ifdef CONFIG_64BIT +#if defined(CONFIG_64BIT) && defined(CONFIG_MMU) /* This is accessed in assembly code. cpumask_var_t would be too complex. */ extern DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); extern char _end[]; diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index c6988983cdf7d7..3ae498779adff3 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -137,7 +137,7 @@ SYM_CODE_START(handle_exception) .Lrestore_kernel_tpsp: csrr tp, CSR_SCRATCH -#ifdef CONFIG_64BIT +#if defined(CONFIG_64BIT) && defined(CONFIG_MMU) /* * The RISC-V kernel does not flush TLBs on all CPUS after each new * vmalloc mapping or kfence_unprotect(), which may result in diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index ea8766c4f3a23c..06596010418a19 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,9 @@ #include "../kernel/head.h" +#if defined(CONFIG_64BIT) && defined(CONFIG_MMU) DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); +#endif struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); From 544a7640e4afdb1a0b3bb86ce0325cfd5eccb525 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Mon, 13 Jul 2026 11:29:52 -0600 Subject: [PATCH 108/521] UPSTREAM: mm/sparse-vmemmap: flush_cache_vmap() after hotplugging vmemmap section_activate() does not flush TLB after populating new vmemmap pages. On most architectures, this is okay. However it is a problem on RISC-V since there the TLB caching non-present entries is permitted, which causes spurious faults on some hardwares. This seems to be most easily reproduced with DEBUG_VM=y and PAGE_POISONING=y, which causes these newly mapped struct pages to be poisoned i.e. written to immediately after mapping. Extend the RISC-V flush_cache_vmap() to also handle the vmemmap range, and call it after hotplugging vmemmap, which gets the possible spurious fault handled in the exception handler. At least for now, the only other architecture with both SPARSEMEM_VMEMMAP and flush_cache_vmap() is PowerPC, which has a similar problem with newly valid PTEs. But there flush_cache_vmap() is just a ptesync. So it should be safe to do this for generic code while having minimal performance impact. Suggested-by: Muchun Song Signed-off-by: Vivian Wang Reviewed-by: Muchun Song Acked-by: David Hildenbrand (Arm) Link: https://patch.msgid.link/20260713-mark-after-vmemmap-populate-v6-2-b945ceba29d4@iscas.ac.cn Signed-off-by: Paul Walmsley (cherry picked from commit 4edd70ee6a7d0408a4e3ac921185779e7605f29c) Signed-off-by: Han Gao --- arch/riscv/include/asm/cacheflush.h | 3 ++- mm/sparse-vmemmap.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 58e787fad029fd..c2b0a2928f0674 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -56,7 +56,8 @@ static inline void mark_new_valid_map(void) #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) { - if (is_vmalloc_or_module_addr((void *)start)) + if (is_vmalloc_or_module_addr((void *)start) || + (start >= VMEMMAP_START && end <= VMEMMAP_END)) mark_new_valid_map(); } #define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end) diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 6eadb9d116e433..b86064d3dd0745 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -564,6 +564,8 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn, if (r < 0) return NULL; + flush_cache_vmap(start, end); + return pfn_to_page(pfn); } From 3e5ede75ca38679f49341f550c5d35fc6211ba4d Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:40:57 +0200 Subject: [PATCH 109/521] UPSTREAM: arch/riscv: vdso: remove CFI landing pad from rt_sigreturn When CONFIG_RISCV_USER_CFI is enabled, the CFI version of the vDSO, has a CFI landing pad instruction at the start of __vdso_rt_sigreturn. This breaks libgcc's unwinding code which matches on the first two instructions. Other unwinders that rely on similar instruction matching may also be affected. Since __vdso_rt_sigreturn is reached as part of signal-return handling rather than via an indirect call/jump from userspace, it does not need a CFI landing pad. Remove it and restore the instruction sequence expected by existing unwinding code. This matches what was done on arm64 in commit 9a964285572b ("arm64: vdso: Don't prefix sigreturn trampoline with a BTI C instruction") for a similar issue. Cc: stable@vger.kernel.org Fixes: 37f57bd3faea ("arch/riscv: compile vdso with landing pad and shadow stack note") Co-authored-by: Joel Stanley Signed-off-by: Aurelien Jarno Signed-off-by: Joel Stanley Link: https://patch.msgid.link/20260623204058.498120-1-aurelien@aurel32.net [pjw@kernel.org: fixed comment style] Signed-off-by: Paul Walmsley (cherry picked from commit e4bf6eb4c7b61db1cf24487e14e6ae8755e61e3d) Signed-off-by: Han Gao --- arch/riscv/kernel/vdso/rt_sigreturn.S | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/vdso/rt_sigreturn.S b/arch/riscv/kernel/vdso/rt_sigreturn.S index e82987dc37394b..d6f96b1abe409b 100644 --- a/arch/riscv/kernel/vdso/rt_sigreturn.S +++ b/arch/riscv/kernel/vdso/rt_sigreturn.S @@ -7,11 +7,19 @@ #include #include +/* + * WARNING: Do NOT add a CFI landing pad at the start of this function. + * Unwinders such as libgcc identify the sigreturn trampoline by matching the + * instruction sequence. Adding a landing pad here would break unwinding from + * signal handlers. + * + * This trampoline is used only for signal return and not via an indirect + * call/jump from userspace, so adding CFI landing pad is unnecessary. + */ .text SYM_FUNC_START(__vdso_rt_sigreturn) .cfi_startproc .cfi_signal_frame - vdso_lpad li a7, __NR_rt_sigreturn ecall .cfi_endproc From 81d1a15567a955d10137dfa267477331cf51bee1 Mon Sep 17 00:00:00 2001 From: Hunter Laux Date: Wed, 6 Apr 2016 00:54:05 -0700 Subject: [PATCH 110/521] BACKPORT: FROMLIST: usb: phy: tegra: Add 38.4MHz clock table entry The Tegra210 uses a 38.4MHz OSC. This clock table entry is required to use the ehci phy on the Jetson TX1. The xtal_freq_count is actually a 12 bit value, so it should be a u16 instead of u8. Signed-off-by: Hunter Laux Link: https://lore.kernel.org/all/1459929245-23449-1-git-send-email-hunterlaux@gmail.com/ Signed-off-by: Kexy Biscuit [Mingcong Bai: Fixed a minor post-6.19 merge conflict in drivers/usb/phy/phy-tegra-usb.c] Signed-off-by: Mingcong Bai --- drivers/usb/phy/phy-tegra-usb.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 00443a7beaeb05..4e027e6ea5ed52 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -238,6 +238,7 @@ struct tegra_xtal_freq { u8 active_delay; u8 utmi_xtal_freq_count; u16 hsic_xtal_freq_count; + u16 xtal_freq_count; u16 debounce; }; @@ -278,6 +279,14 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .hsic_xtal_freq_count = 0x3E0, .debounce = 0xFDE8, }, + { + .freq = 38400000, + .enable_delay = 0x00, + .stable_count = 0x00, + .active_delay = 0x18, + .xtal_freq_count = 0x177, + .debounce = 0xBB80, + }, }; static inline struct tegra_usb_phy *to_tegra_usb_phy(struct usb_phy *u_phy) From a862bf46fde52aa95a517a814725cd6188f0d881 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 8 Nov 2023 10:46:13 +0800 Subject: [PATCH 111/521] BACKPORT: FROMLIST: drm/Makefile: Move tiny drivers before native drivers After commit 60aebc9559492cea ("drivers/firmware: Move sysfb_init() from device_initcall to subsys_initcall_sync") some Lenovo laptops get a blank screen until the display manager starts. This regression occurs with such a Kconfig combination: CONFIG_SYSFB=y CONFIG_SYSFB_SIMPLEFB=y CONFIG_DRM_SIMPLEDRM=y CONFIG_DRM_I915=y # Or other native drivers such as radeon, amdgpu If replace CONFIG_DRM_SIMPLEDRM with CONFIG_FB_SIMPLE (they use the same device), there is no blank screen. The root cause is the initialization order, and this order depends on the Makefile. FB_SIMPLE is before native DRM drivers (e.g. i915, radeon, amdgpu, and so on), but DRM_SIMPLEDRM is after them. Thus, if we use FB_SIMPLE, I915 will takeover FB_SIMPLE, then no problem; and if we use DRM_SIMPLEDRM, DRM_SIMPLEDRM will try to takeover I915, but fails to work. So we can move the "tiny" directory before native DRM drivers to solve this problem. Fixes: 60aebc9559492cea ("drivers/firmware: Move sysfb_init() from device_initcall to subsys_initcall_sync") Closes: https://lore.kernel.org/dri-devel/ZUnNi3q3yB3zZfTl@P70.localdomain/T/#t Reported-by: Jaak Ristioja Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/20231108024613.2898921-1-chenhuacai@loongson.cn/ [Kexy: Resolved minor conflict in drivers/gpu/drm/Makefile] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index e97faabcd78300..16b042dc87a982 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -173,6 +173,7 @@ obj-y += clients/ obj-y += display/ obj-$(CONFIG_DRM_TTM) += ttm/ obj-$(CONFIG_DRM_SCHED) += scheduler/ +obj-y += tiny/ obj-$(CONFIG_DRM_RADEON)+= radeon/ obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/ obj-$(CONFIG_DRM_AMDGPU)+= amd/amdxcp/ @@ -216,7 +217,6 @@ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ obj-y += hisilicon/ obj-y += mxsfb/ obj-y += sysfb/ -obj-y += tiny/ obj-$(CONFIG_DRM_PL111) += pl111/ obj-$(CONFIG_DRM_TVE200) += tve200/ obj-$(CONFIG_DRM_ADP) += adp/ From c6cf74d7bcf8ed05ed992209ce403ecf2b19d965 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 20 Feb 2024 15:49:58 +0800 Subject: [PATCH 112/521] FROMLIST: drm/radeon: Call mmiowb() at the end of radeon_ring_commit() Commit fb24ea52f78e0d595852e ("drivers: Remove explicit invocations of mmiowb()") remove all mmiowb() in drivers, but it says: "NOTE: mmiowb() has only ever guaranteed ordering in conjunction with spin_unlock(). However, pairing each mmiowb() removal in this patch with the corresponding call to spin_unlock() is not at all trivial, so there is a small chance that this change may regress any drivers incorrectly relying on mmiowb() to order MMIO writes between CPUs using lock-free synchronisation." The mmio in radeon_ring_commit() is protected by a mutex rather than a spinlock, but in the mutex fastpath it behaves similar to spinlock and need a mmiowb() to make sure the wptr is up-to-date for hardware. Without this, we get such an error when run 'glxgears' on weak ordering architectures such as LoongArch: radeon 0000:04:00.0: ring 0 stalled for more than 10324msec radeon 0000:04:00.0: ring 3 stalled for more than 10240msec radeon 0000:04:00.0: GPU lockup (current fence id 0x000000000001f412 last fence id 0x000000000001f414 on ring 3) radeon 0000:04:00.0: GPU lockup (current fence id 0x000000000000f940 last fence id 0x000000000000f941 on ring 0) radeon 0000:04:00.0: scheduling IB failed (-35). [drm:radeon_gem_va_ioctl [radeon]] *ERROR* Couldn't update BO_VA (-35) radeon 0000:04:00.0: scheduling IB failed (-35). [drm:radeon_gem_va_ioctl [radeon]] *ERROR* Couldn't update BO_VA (-35) radeon 0000:04:00.0: scheduling IB failed (-35). [drm:radeon_gem_va_ioctl [radeon]] *ERROR* Couldn't update BO_VA (-35) radeon 0000:04:00.0: scheduling IB failed (-35). [drm:radeon_gem_va_ioctl [radeon]] *ERROR* Couldn't update BO_VA (-35) radeon 0000:04:00.0: scheduling IB failed (-35). [drm:radeon_gem_va_ioctl [radeon]] *ERROR* Couldn't update BO_VA (-35) radeon 0000:04:00.0: scheduling IB failed (-35). [drm:radeon_gem_va_ioctl [radeon]] *ERROR* Couldn't update BO_VA (-35) radeon 0000:04:00.0: scheduling IB failed (-35). [drm:radeon_gem_va_ioctl [radeon]] *ERROR* Couldn't update BO_VA (-35) Cc: stable@vger.kernel.org Signed-off-by: Tianyang Zhang Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/20240220074958.3288170-1-chenhuacai@loongson.cn/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/radeon/radeon_ring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 581ae20c46e4b5..346f0e49bb2eaa 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -185,6 +185,7 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring, if (hdp_flush && rdev->asic->mmio_hdp_flush) rdev->asic->mmio_hdp_flush(rdev); radeon_ring_set_wptr(rdev, ring); + mmiowb(); /* Make sure wptr is up-to-date for hw */ } /** From 5209798ddda2a7937db4411f94fb8841943bb4c1 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Tue, 7 May 2024 15:43:57 +0800 Subject: [PATCH 113/521] FROMLIST: LoongArch: Update the flush cache policy fix when LoongArch s3 resume, Can't find image information Signed-off-by: Li Jun Signed-off-by: Baoqi Zhang Signed-off-by: Jianmin Lv Signed-off-by: Biao Dong Link: https://lore.kernel.org/all/20240507074357.2156083-1-lijun01@kylinos.cn/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/mm/cache.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/mm/cache.c b/arch/loongarch/mm/cache.c index 06dc570eb429e0..2b62fa9de99e40 100644 --- a/arch/loongarch/mm/cache.c +++ b/arch/loongarch/mm/cache.c @@ -53,6 +53,27 @@ static void flush_cache_leaf(unsigned int leaf) } while (--nr_nodes > 0); } +static void flush_cache_last_level(unsigned int leaf) +{ + u64 addr; + int i, j, nr_nodes; + struct cache_desc *cdesc = current_cpu_data.cache_leaves + leaf; + + nr_nodes = loongson_sysconf.nr_nodes; + + addr = CSR_DMW1_BASE; + iocsr_write32(0x1, 0x280); + do { + for (i = 0; i < (cdesc->ways * 3); i++) { + for (j = 0; j < (cdesc->sets); j++) { + *(volatile u32 *)addr; + addr += cdesc->linesz; + } + } + addr += 0x100000000000; + } while (--nr_nodes > 0); +} + asmlinkage __visible void __flush_cache_all(void) { int leaf; @@ -61,7 +82,7 @@ asmlinkage __visible void __flush_cache_all(void) leaf = cache_present - 1; if (cache_inclusive(cdesc + leaf)) { - flush_cache_leaf(leaf); + flush_cache_last_level(leaf); return; } From c0f83fd42f56f6ce7e16cd41a82162d8d7135e79 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 17 Dec 2024 01:35:14 +0800 Subject: [PATCH 114/521] FROMLIST: mips: disable CRASH_DUMP by default On MIPS, the space of a crash dump kernel needs to be manually specified by both the debugee and the crash dump kernel PHYSICAL_START config option, and the default PHYSICAL_START config will make the kernel load quite higher, which isn't acceptable when using as a daily use kernel (and will even confuse some naive Loongson-3 bootloaders). So I don't think a MIPS kernel should be built as a crash dump kernel by default, therefore this patch disables selecting CRASH_DUMP by default. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/all/20241216173514.495638-1-uwu@icenowy.me/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4364f3dba688fe..17d820e8163a18 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2891,9 +2891,6 @@ config ARCH_SUPPORTS_KEXEC config ARCH_SUPPORTS_CRASH_DUMP def_bool y -config ARCH_DEFAULT_CRASH_DUMP - def_bool y - config PHYSICAL_START hex "Physical address where the kernel is loaded" default "0xffffffff84000000" From 3ec69396b2c780180d18a2addf2815cfb2ca2a10 Mon Sep 17 00:00:00 2001 From: Lyle Li Date: Thu, 2 Jan 2025 15:54:19 +0800 Subject: [PATCH 115/521] FROMLIST: x86/fpu: Fix the os panic issue caused by the XGETBV instruction The callers of the xfeatures_in_use function must ensure that the current processor has the X86_FEATURE_XGETBV1 feature. However, in some places where xfeatures_in_use is called, there is no check to see if the processor supports this feature, leading to the execution of the XGETBV XCR1 instruction on processors that do not support this feature, triggering a #GP exception, and ultimately causing an OS panic. To fix this issue, a check for the X86_FEATURE_XGETBV1 feature has been added before calling xfeatures_in_use. Fixes: ae6012d72fa6 ("x86/pkeys: Ensure updated PKRU value is XRSTOR'd") Fixes: 30d02551ba4f ("x86/fpu: Optimize out sigframe xfeatures when in init state") Signed-off-by: Lyle Li Signed-off-by: Tony W Wang-oc Link: https://lore.kernel.org/all/20250102075419.2559-1-TonyWWang-oc@zhaoxin.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/x86/kernel/fpu/xstate.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/fpu/xstate.h b/arch/x86/kernel/fpu/xstate.h index 38a2862f09d35b..dbc20466507e37 100644 --- a/arch/x86/kernel/fpu/xstate.h +++ b/arch/x86/kernel/fpu/xstate.h @@ -93,6 +93,9 @@ static inline int update_pkru_in_sigframe(struct xregs_state __user *buf, u32 pk if (unlikely(!cpu_feature_enabled(X86_FEATURE_OSPKE))) return 0; + if (!cpu_feature_enabled(X86_FEATURE_XGETBV1)) + return 0; + /* Mark PKRU as in-use so that it is restored correctly. */ err = set_xfeature_in_sigframe(buf, XFEATURE_MASK_PKRU); if (err) @@ -306,7 +309,7 @@ static inline int xsave_to_user_sigframe(struct xregs_state __user *buf, u32 pkr int err; /* Optimize away writing unnecessary xfeatures: */ - if (fpu_state_size_dynamic()) + if (cpu_feature_enabled(X86_FEATURE_XGETBV1) && fpu_state_size_dynamic()) mask &= xfeatures_need_sigframe_write(); lmask = mask; From f1abfa7c5dfe31f5de351b640fc3c8387fbffa7f Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 31 Jan 2025 18:06:30 +0800 Subject: [PATCH 116/521] FROMLIST: USB: core: Enable root_hub's remote wakeup for wakeup sources Now we only enable the remote wakeup function for the USB wakeup source itself at usb_port_suspend(). But on pre-XHCI controllers this is not enough to enable the S3 wakeup function for USB keyboards, so we also enable the root_hub's remote wakeup (and disable it on error). Frankly this is unnecessary for XHCI, but enable it unconditionally make code simple and seems harmless. Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/20250131100630.342995-1-chenhuacai@loongson.cn/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/usb/core/hub.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 24960ba9caa915..e90a72bd1cc348 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3523,6 +3523,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (PMSG_IS_AUTO(msg)) goto err_wakeup; } + usb_enable_remote_wakeup(udev->bus->root_hub); } /* disable USB2 hardware LPM */ @@ -3586,8 +3587,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) /* Try to enable USB2 hardware LPM again */ usb_enable_usb2_hardware_lpm(udev); - if (udev->do_remote_wakeup) + if (udev->do_remote_wakeup) { (void) usb_disable_remote_wakeup(udev); + (void) usb_disable_remote_wakeup(udev->bus->root_hub); + } err_wakeup: /* System sleep transitions should never fail */ From 9ef838d5700a6d5fbab02f883ed696d51e18d655 Mon Sep 17 00:00:00 2001 From: WangYuli Date: Tue, 18 Mar 2025 14:11:25 +0800 Subject: [PATCH 117/521] FROMLIST: scsi: Bypass certain SCSI commands on disks with "use_192_bytes_for_3f" attribute On some external USB hard drives, mounting can fail if "lshw" is executed during the process. This occurs because data sent to the device's output endpoint in certain abnormal scenarios does not receive a response, leading to a mount timeout. [ Description of "use_192_bytes_for_3f" in the kernel code: ] /* * Many disks only accept MODE SENSE transfer lengths of * 192 bytes (that's what Windows uses). */ sdev->use_192_bytes_for_3f = 1; The kernel's SCSI driver, when handling devices with this attribute, sends commands with a length of 192 bytes like this: if (sdp->use_192_bytes_for_3f) res = sd_do_mode_sense(sdp, 0, 0x3F, buffer, 192, &data, NULL); However, "lshw" disregards the "use_192_bytes_for_3f" attribute and transmits data with a length of 0xff bytes via ioctl, which can cause some hard drives to hang and become unusable. To resolve this issue, prevent commands with a length of 0xff bytes from being queued via ioctl when it detects the "use_192_bytes_for_3f" attribute on the device. The hard drive device identified with the issue is Lenovo USB 17ef:4531. Tested on HONOR NBLK-WAX9X (C234) Notebook with AMD Ryzen 7 3700U. [ Kernel logs: ] 2024-10-31 13:36:11 localhost kernel: [ 25.770091] usb 2-2: new SuperSpeed Gen 1 USB device number 2 using xhci_hcd 2024-10-31 13:36:11 localhost kernel: [ 25.798558] usb 2-2: New USB device found, idVendor=17ef, idProduct=4531, bcdDevice= 5.12 2024-10-31 13:36:11 localhost kernel: [ 25.798562] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 2024-10-31 13:36:11 localhost kernel: [ 25.798564] usb 2-2: Product: Lenovo Portable HDD 2024-10-31 13:36:11 localhost kernel: [ 25.798566] usb 2-2: Manufacturer: Lenovo 2024-10-31 13:36:11 localhost kernel: [ 25.798567] usb 2-2: SerialNumber: 000000001E4C 2024-10-31 13:36:11 localhost kernel: [ 25.820244] usb-storage 2-2:1.0: USB Mass Storage device detected 2024-10-31 13:36:11 localhost kernel: [ 25.820457] scsi host0: usb-storage 2-2:1.0 2024-10-31 13:36:11 localhost kernel: [ 25.820633] usbcore: registered new interface driver usb-storage 2024-10-31 13:36:11 localhost kernel: [ 25.825598] usbcore: registered new interface driver uas 2024-10-31 13:36:14 localhost kernel: [ 28.852179] scsi 0:0:0:0: Direct-Access Lenovo USB Hard Drive 0006 PQ: 0 ANSI: 2 2024-10-31 13:36:14 localhost kernel: [ 28.852961] sd 0:0:0:0: Attached scsi generic sg0 type 0 2024-10-31 13:36:14 localhost kernel: [ 28.891218] sd 0:0:0:0: [sda] 976773164 512-byte logical blocks: (500 GB/466 GiB) 2024-10-31 13:36:14 localhost kernel: [ 28.906892] sd 0:0:0:0: [sda] Write Protect is off 2024-10-31 13:36:14 localhost kernel: [ 28.906896] sd 0:0:0:0: [sda] Mode Sense: 03 00 00 00 2024-10-31 13:36:14 localhost kernel: [ 28.922606] sd 0:0:0:0: [sda] No Caching mode page found 2024-10-31 13:36:14 localhost kernel: [ 28.922612] sd 0:0:0:0: [sda] Assuming drive cache: write through 2024-10-31 13:36:14 localhost kernel: [ 29.007816] sda: sda1 2024-10-31 13:36:15 localhost kernel: [ 30.180380] sd 0:0:0:0: [sda] Attached SCSI disk 2024-10-31 13:36:16 localhost kernel: [ 30.722863] snd_hda_codec_realtek hdaudioC1D0: hda_codec_setup_stream: NID=0x3, stream=0x5, channel=0, format=0x4011 2024-10-31 13:36:16 localhost kernel: [ 30.734139] snd_hda_codec_realtek hdaudioC1D0: hda_codec_setup_stream: NID=0x2, stream=0x5, channel=0, format=0x4011 2024-10-31 13:36:17 localhost kernel: [ 31.396011] start_addr=(0x20000), end_addr=(0x40000), buffer_size=(0x20000), smp_number_max=(16384) 2024-10-31 13:36:18 localhost kernel: [ 32.933537] snd_hda_codec_realtek hdaudioC1D0: hda_codec_cleanup_stream: NID=0x3 2024-10-31 13:36:18 localhost kernel: [ 32.933541] snd_hda_codec_realtek hdaudioC1D0: hda_codec_cleanup_stream: NID=0x2 2024-10-31 13:36:39 localhost kernel: [ 54.242220] usb 2-2: reset SuperSpeed Gen 1 USB device number 2 using xhci_hcd 2024-10-31 13:36:50 localhost kernel: [ 64.408879] start_addr=(0x20000), end_addr=(0x40000), buffer_size=(0x20000), smp_number_max=(16384) 2024-10-31 13:37:11 localhost kernel: [ 85.466479] usb 2-2: reset SuperSpeed Gen 1 USB device number 2 using xhci_hcd 2024-10-31 13:37:11 localhost kernel: [ 85.490248] sd 0:0:0:0: [sda] tag#0 FAILED Result: hostbyte=DID_TIME_OUT driverbyte=DRIVER_OK 2024-10-31 13:37:11 localhost kernel: [ 85.490255] sd 0:0:0:0: [sda] tag#0 CDB: Read(10) 28 00 00 00 00 20 00 00 08 00 2024-10-31 13:37:11 localhost kernel: [ 85.490258] print_req_error: I/O error, dev sda, sector 32 2024-10-31 13:37:33 localhost kernel: [ 107.432186] start_addr=(0x20000), end_addr=(0x40000), buffer_size=(0x20000), smp_number_max=(16384) 2024-10-31 13:37:41 localhost kernel: [ 116.194201] usb 2-2: reset SuperSpeed Gen 1 USB device number 2 using xhci_hcd 2024-10-31 13:37:49 localhost kernel: [ 123.555484] dolphin[7271]: segfault at 10 ip 00007fcccc0d7f76 sp 00007ffe8004b860 error 4 in libKF5CoreAddons.so.5.102.0[7fcccc0a5000+83000] 2024-10-31 13:37:49 localhost kernel: [ 123.555502] Code: d6 90 66 90 41 54 41 89 d4 55 48 89 fd 53 48 89 f3 e8 8e 94 01 00 ba 04 00 00 00 48 89 de 48 89 c7 e8 4e 8f 01 00 84 c0 75 2a <48> 8b 7d 10 48 85 ff 74 21 45 89 e1 48 89 da 48 89 ee 5b 41 b8 01 2024-10-31 13:38:11 localhost kernel: [ 146.229510] usb 2-2: USB disconnect, device number 2 2024-10-31 13:38:11 localhost kernel: [ 146.237993] scsi 0:0:0:0: rejecting I/O to dead device 2024-10-31 13:38:11 localhost kernel: [ 146.238003] print_req_error: I/O error, dev sda, sector 32 2024-10-31 13:38:11 localhost kernel: [ 146.238009] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238029] scsi 0:0:0:0: rejecting I/O to dead device 2024-10-31 13:38:11 localhost kernel: [ 146.238030] print_req_error: I/O error, dev sda, sector 36 2024-10-31 13:38:11 localhost kernel: [ 146.238032] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238045] scsi 0:0:0:0: rejecting I/O to dead device 2024-10-31 13:38:11 localhost kernel: [ 146.238047] print_req_error: I/O error, dev sda, sector 6291480 2024-10-31 13:38:11 localhost kernel: [ 146.238062] Buffer I/O error on dev sda1, logical block 786431, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238168] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238170] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238175] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238176] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238184] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238185] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238199] Buffer I/O error on dev sda, logical block 40, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238201] Buffer I/O error on dev sda, logical block 41, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238205] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238206] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238210] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238211] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238215] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238217] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238220] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238221] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238224] Buffer I/O error on dev sda, logical block 8, async page read 2024-10-31 13:38:11 localhost kernel: [ 146.238226] Buffer I/O error on dev sda, logical block 9, async page read 2024-10-31 13:38:12 localhost kernel: [ 146.482007] snd_hda_codec_realtek hdaudioC1D0: hda_codec_setup_stream: NID=0x3, stream=0x5, channel=0, format=0x4011 2024-10-31 13:38:12 localhost kernel: [ 146.494064] snd_hda_codec_realtek hdaudioC1D0: hda_codec_setup_stream: NID=0x2, stream=0x5, channel=0, format=0x4011 2024-10-31 13:38:15 localhost kernel: [ 150.065848] snd_hda_codec_realtek hdaudioC1D0: hda_codec_cleanup_stream: NID=0x3 2024-10-31 13:38:15 localhost kernel: [ 150.065852] snd_hda_codec_realtek hdaudioC1D0: hda_codec_cleanup_stream: NID=0x2 2024-10-31 13:38:26 localhost kernel: [ 160.433037] start_addr=(0x20000), end_addr=(0x40000), buffer_size=(0x20000), smp_number_max=(16384) 2024-10-31 13:39:29 localhost kernel: [ 223.444589] start_addr=(0x20000), end_addr=(0x40000), buffer_size=(0x20000), smp_number_max=(16384) Link: https://linux-hardware.org/?id=usb:17ef-4531 Link: https://lore.kernel.org/all/80ef917b-3680-4f85-93ba-c92d2b69ebaa@rowland.harvard.edu/ Link: https://lore.kernel.org/all/ad4bd008-8d0d-439b-879c-e9cf4c89ec56@acm.org/ Link: https://lore.kernel.org/all/4EB8ECD64F601331+e2f01a1f-8da5-4e7b-b909-d920a792756a@uniontech.com/ Reported-by: Xinwei Zhou Co-developed-by: Xu Rao Signed-off-by: Xu Rao Tested-by: Yujing Ming Signed-off-by: WangYuli Link: https://lore.kernel.org/all/798FB027101C5650+20250318061125.477498-1-wangyuli@uniontech.com/ Link: https://github.com/AOSC-Tracking/linux/pull/7 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/scsi/scsi_lib.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 85eef401925a27..27c4b02d76d5d4 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1613,6 +1613,7 @@ static void scsi_complete(struct request *rq) static enum scsi_qc_status scsi_dispatch_cmd(struct scsi_cmnd *cmd) { struct Scsi_Host *host = cmd->device->host; + struct scsi_device *sdev = cmd->device; int rtn = 0; atomic_inc(&cmd->device->iorequest_cnt); @@ -1661,6 +1662,17 @@ static enum scsi_qc_status scsi_dispatch_cmd(struct scsi_cmnd *cmd) goto done; } + /* + * Before we queue this command, check attribute use_192_bytes_for_3f. + * Because transmits data with a length of 0xff bytes via ioctl may + * cause some hard drives to hang and become unusable. + */ + if (cmd->cmnd[0] == MODE_SENSE && sdev->use_192_bytes_for_3f && + cmd->cmnd[2] == 0x3f && cmd->cmnd[4] != 192) { + cmd->result = DID_ABORT << 16; + goto done; + } + if (unlikely(host->shost_state == SHOST_DEL)) { cmd->result = (DID_NO_CONNECT << 16); goto done; From ef79d71b3887db5ef2a9172c6bbae0fb57288247 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 11 May 2025 16:34:13 +0800 Subject: [PATCH 118/521] FROMLIST: PCI: Prevent LS7A Bus Master clearing on kexec This is similar to commit 62b6dee1b44a ("PCI/portdrv: Prevent LS7A Bus Master clearing on shutdown"), which prevents LS7A Bus Master clearing on kexec. The key point of this is to work around the LS7A defect that clearing PCI_COMMAND_MASTER prevents MMIO requests from going downstream, and we may need to do that even after .shutdown(), e.g., to print console messages. And in this case we rely on .shutdown() for the downstream devices to disable interrupts and DMA. Only skip Bus Master clearing on bridges because endpoint devices still need it. Cc: stable@vger.kernel.org Signed-off-by: Ming Wang Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/20250511083413.3326421-3-chenhuacai@loongson.cn/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e3f59001785a17..682c34a174dade 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -555,7 +555,7 @@ static void pci_device_shutdown(struct device *dev) * If it is not a kexec reboot, firmware will hit the PCI * devices with big hammer and stop their DMA any way. */ - if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot)) + if (kexec_in_progress && !pci_is_bridge(pci_dev) && (pci_dev->current_state <= PCI_D3hot)) pci_clear_master(pci_dev); } From 5d4aa331dedbd517bbe142d76c521629eca67734 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 17 Aug 2025 22:19:09 +0800 Subject: [PATCH 119/521] REVERT: Revert "Mark xe driver as BROKEN if kernel page size is not 4kB" This reverts commit 022906afdf90327bce33d52fb4fb41b6c7d618fb. Signed-off-by: Mingcong Bai --- drivers/gpu/drm/xe/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig index 4d7dcaff2b9153..03edbfe22ccf6d 100644 --- a/drivers/gpu/drm/xe/Kconfig +++ b/drivers/gpu/drm/xe/Kconfig @@ -5,7 +5,6 @@ config DRM_XE depends on KUNIT || !KUNIT depends on INTEL_VSEC || !INTEL_VSEC depends on X86_PLATFORM_DEVICES || !(X86 && ACPI) - depends on PAGE_SIZE_4KB || COMPILE_TEST || BROKEN select INTERVAL_TREE # we need shmfs for the swappable backing store, and in particular # the shmem_readpage() which depends upon tmpfs From 1af88e63b2ccaee54672bfc89dc8bef3d34a5512 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Feb 2025 10:00:18 +0800 Subject: [PATCH 120/521] BACKPORT: FROMLIST: drm/xe/bo: fix alignment with non-4K kernel page sizes The bo/ttm interfaces with kernel memory mapping from dedicated GPU memory. It is not correct to assume that SZ_4K would suffice for page alignment as there are a few hardware platforms that commonly uses non-4K pages - for instance, currently, Loongson 3A5000/6000 devices (of the LoongArch architecture) commonly uses 16K kernel pages. Per my testing Intel Xe/Arc families of GPUs works on at least Loongson 3A6000 platforms so long as "Above 4G Decoding" and "Resizable BAR" were enabled in the EFI firmware settings. I tested this patch series on my Loongson XA61200 (3A6000) motherboard with an Intel Arc A750 GPU. Without this fix, the kernel will hang at a kernel BUG(): [ 7.425445] ------------[ cut here ]------------ [ 7.430032] kernel BUG at drivers/gpu/drm/drm_gem.c:181! [ 7.435330] Oops - BUG[#1]: [ 7.438099] CPU: 0 UID: 0 PID: 102 Comm: kworker/0:4 Tainted: G E 6.13.3-aosc-main-00336-g60829239b300-dirty #3 [ 7.449511] Tainted: [E]=UNSIGNED_MODULE [ 7.453402] Hardware name: Loongson Loongson-3A6000-HV-7A2000-1w-V0.1-EVB/Loongson-3A6000-HV-7A2000-1w-EVB-V1.21, BIOS Loongson-UDK2018-V4.0.05756-prestab [ 7.467144] Workqueue: events work_for_cpu_fn [ 7.471472] pc 9000000001045fa4 ra ffff8000025331dc tp 90000001010c8000 sp 90000001010cb960 [ 7.479770] a0 900000012a3e8000 a1 900000010028c000 a2 000000000005d000 a3 0000000000000000 [ 7.488069] a4 0000000000000000 a5 0000000000000000 a6 0000000000000000 a7 0000000000000001 [ 7.496367] t0 0000000000001000 t1 9000000001045000 t2 0000000000000000 t3 0000000000000000 [ 7.504665] t4 0000000000000000 t5 0000000000000000 t6 0000000000000000 t7 0000000000000000 [ 7.504667] t8 0000000000000000 u0 90000000029ea7d8 s9 900000012a3e9360 s0 900000010028c000 [ 7.504668] s1 ffff800002744000 s2 0000000000000000 s3 0000000000000000 s4 0000000000000001 [ 7.504669] s5 900000012a3e8000 s6 0000000000000001 s7 0000000000022022 s8 0000000000000000 [ 7.537855] ra: ffff8000025331dc ___xe_bo_create_locked+0x158/0x3b0 [xe] [ 7.544893] ERA: 9000000001045fa4 drm_gem_private_object_init+0xcc/0xd0 [ 7.551639] CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE) [ 7.557785] PRMD: 00000004 (PPLV0 +PIE -PWE) [ 7.562111] EUEN: 00000000 (-FPE -SXE -ASXE -BTE) [ 7.566870] ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7) [ 7.571628] ESTAT: 000c0000 [BRK] (IS= ECode=12 EsubCode=0) [ 7.577163] PRID: 0014d000 (Loongson-64bit, Loongson-3A6000-HV) [ 7.583128] Modules linked in: xe(E+) drm_gpuvm(E) drm_exec(E) drm_buddy(E) gpu_sched(E) drm_suballoc_helper(E) drm_display_helper(E) loongson(E) r8169(E) cec(E) rc_core(E) realtek(E) i2c_algo_bit(E) tpm_tis_spi(E) led_class(E) hid_generic(E) drm_ttm_helper(E) ttm(E) drm_client_lib(E) drm_kms_helper(E) sunrpc(E) la_ow_syscall(E) i2c_dev(E) [ 7.613049] Process kworker/0:4 (pid: 102, threadinfo=00000000bc26ebd1, task=0000000055480707) [ 7.621606] Stack : 0000000000000000 3030303a6963702b 000000000005d000 0000000000000000 [ 7.629563] 0000000000000001 0000000000000000 0000000000000000 8e1bfae42b2f7877 [ 7.637519] 000000000005d000 900000012a3e8000 900000012a3e9360 0000000000000000 [ 7.645475] ffffffffffffffff 0000000000000000 0000000000022022 0000000000000000 [ 7.653431] 0000000000000001 ffff800002533660 0000000000022022 9000000000234470 [ 7.661386] 90000001010cba28 0000000000001000 0000000000000000 000000000005c300 [ 7.669342] 900000012a3e8000 0000000000000000 0000000000000001 900000012a3e8000 [ 7.677298] ffffffffffffffff 0000000000022022 900000012a3e9498 ffff800002533a14 [ 7.685254] 0000000000022022 0000000000000000 900000000209c000 90000000010589e0 [ 7.693209] 90000001010cbab8 ffff8000027c78c0 fffffffffffff000 900000012a3e8000 [ 7.701165] ... [ 7.703588] Call Trace: [ 7.703590] [<9000000001045fa4>] drm_gem_private_object_init+0xcc/0xd0 [ 7.712496] [] ___xe_bo_create_locked+0x154/0x3b0 [xe] [ 7.719268] [] __xe_bo_create_locked+0x228/0x304 [xe] [ 7.725951] [] xe_bo_create_pin_map_at_aligned+0x70/0x1b0 [xe] [ 7.733410] [] xe_managed_bo_create_pin_map+0x34/0xcc [xe] [ 7.740522] [] xe_managed_bo_create_from_data+0x44/0xb0 [xe] [ 7.747807] [] xe_uc_fw_init+0x3ec/0x904 [xe] [ 7.753814] [] xe_guc_init+0x30/0x3dc [xe] [ 7.759553] [] xe_uc_init+0x20/0xf0 [xe] [ 7.765121] [] xe_gt_init_hwconfig+0x5c/0xd0 [xe] [ 7.771461] [] xe_device_probe+0x240/0x588 [xe] [ 7.777627] [] xe_pci_probe+0x6c0/0xa6c [xe] [ 7.783540] [<9000000000e9828c>] local_pci_probe+0x4c/0xb4 [ 7.788989] [<90000000002aa578>] work_for_cpu_fn+0x20/0x40 [ 7.794436] [<90000000002aeb50>] process_one_work+0x1a4/0x458 [ 7.800143] [<90000000002af5a0>] worker_thread+0x304/0x3fc [ 7.805591] [<90000000002bacac>] kthread+0x114/0x138 [ 7.810520] [<9000000000241f64>] ret_from_kernel_thread+0x8/0xa4 [ 7.816489] [ 7.817961] Code: 4c000020 29c3e2f9 53ff93ff <002a0001> 0015002c 03400000 02ff8063 29c04077 001500f7 [ 7.827651] [ 7.829140] ---[ end trace 0000000000000000 ]--- Revise all instances of `SZ_4K' with `PAGE_SIZE' and revise the call to `drm_gem_private_object_init()' in `*___xe_bo_create_locked()' (last call before BUG()) to use `size_t aligned_size' calculated from `PAGE_SIZE' to fix the above error. Cc: Fixes: 4e03b584143e ("drm/xe/uapi: Reject bo creation of unaligned size") Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Tested-by: Mingcong Bai Tested-by: Haien Liang <27873200@qq.com> Tested-by: Shirong Liu Tested-by: Haofeng Wu Link: https://github.com/FanFansfan/loongson-linux/commit/22c55ab3931c32410a077b3ddb6dca3f28223360 Co-developed-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Mingcong Bai [Mingcong Bai: Resolved a minor merge conflict post-6.16 in drivers/gpu/drm/xe/xe_bo.c] Link: https://lore.kernel.org/all/20250613-upstream-xe-non-4k-v2-v2-1-934f82249f8a@aosc.io/ Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/gpu/drm/xe/xe_bo.c ] Signed-off-by: Mingcong Bai --- drivers/gpu/drm/xe/xe_bo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 6b518858538f57..37cdf3b99e939b 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -2339,9 +2339,9 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, flags |= XE_BO_FLAG_INTERNAL_64K; alignment = align >> PAGE_SHIFT; } else { - aligned_size = ALIGN(size, SZ_4K); + aligned_size = ALIGN(size, PAGE_SIZE); flags &= ~XE_BO_FLAG_INTERNAL_64K; - alignment = SZ_4K >> PAGE_SHIFT; + alignment = PAGE_SIZE >> PAGE_SHIFT; } if (type == ttm_bo_type_device && aligned_size != size) { @@ -2370,7 +2370,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, /* Initialize purge advisory state */ bo->purgeable.state = XE_MADV_PURGEABLE_WILLNEED; - drm_gem_private_object_init(&xe->drm, &bo->ttm.base, size); + drm_gem_private_object_init(&xe->drm, &bo->ttm.base, aligned_size); if (resv) { ctx.allow_res_evict = !(flags & XE_BO_FLAG_NO_RESV_EVICT); From 20ccca803d3851b3c0a84e36626715f7d6d5ccd1 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Feb 2025 10:00:19 +0800 Subject: [PATCH 121/521] BACKPORT: FROMLIST: drm/xe/guc: use SZ_4K for alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the "Firmware" chapter in "drm/xe Intel GFX Driver", as well as "Volume 8: Command Stream Programming" in "Intel® Arc™ A-Series Graphics and Intel Data Center GPU Flex Series Open-Source Programmer's Reference Manual For the discrete GPUs code named "Alchemist" and "Arctic Sound-M"" and "Intel® Iris® Xe MAX Graphics Open Source Programmer's Reference Manual For the 2020 Discrete GPU formerly named "DG1"": "The RINGBUF register sets (defined in Memory Interface Registers) are used to specify the ring buffer memory areas. The ring buffer must start on a 4KB boundary and be allocated in linear memory. The length of any one ring buffer is limited to 2MB." The Graphics micro (μ) Controller (GuC) really expects command buffers aligned to 4K boundaries. Current code uses `PAGE_SIZE' as an assumed alignment reference but 4K kernel page sizes is by no means a guarantee. On 16K-paged kernels, this causes driver failures after loading the GuC firmware: [ 7.398317] xe 0000:09:00.0: [drm] Found dg2/g10 (device ID 56a1) display version 13.00 stepping C0 [ 7.410429] xe 0000:09:00.0: [drm] Using GuC firmware from i915/dg2_guc_70.bin version 70.36.0 [ 10.719989] xe 0000:09:00.0: [drm] *ERROR* GT0: load failed: status = 0x800001EC, time = 3297ms, freq = 2400MHz (req 2400MHz), done = 0 [ 10.732106] xe 0000:09:00.0: [drm] *ERROR* GT0: load failed: status: Reset = 0, BootROM = 0x76, UKernel = 0x01, MIA = 0x00, Auth = 0x02 [ 10.744214] xe 0000:09:00.0: [drm] *ERROR* CRITICAL: Xe has declared device 0000:09:00.0 as wedged. Please file a _new_ bug report at https://gitlab.freedesktop.org/drm/xe/kernel/issues/new [ 10.828908] xe 0000:09:00.0: [drm] *ERROR* GT0: GuC mmio request 0x4100: no reply 0x4100 Correct this by revising all instances of `PAGE_SIZE' to `SZ_4K' and revise `PAGE_ALIGN()' calls to `ALIGN()' with `SZ_4K' as the second argument (overriding `PAGE_SIZE'). Cc: stable@vger.kernel.org Fixes: 84d15f426110 ("drm/xe/guc: Add capture size check in GuC log buffer") Fixes: 9c8c7a7e6f1f ("drm/xe/guc: Prepare GuC register list and update ADS size for error capture") Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Tested-by: Mingcong Bai Tested-by: Haien Liang <27873200@qq.com> Tested-by: Shirong Liu Tested-by: Haofeng Wu Link: https://github.com/FanFansfan/loongson-linux/commit/22c55ab3931c32410a077b3ddb6dca3f28223360 Co-developed-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Shang Yatsen <429839446@qq.com> Co-developed-by: Kexy Biscuit Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai Reviewed-by: Matthew Brost Link: https://lore.kernel.org/all/20250613-upstream-xe-non-4k-v2-v2-1-934f82249f8a@aosc.io/ Signed-off-by: Mingcong Bai [Mingcong Bai: Resolved a minor merge conflict post-6.16 in drivers/gpu/drm/xe/xe_guc_ads.c] [Mingcong Bai: Resolved a minor merge conflict since 6.18 in drivers/gpu/drm/xe/xe_guc.c.] [Mingcong Bai: Resolved a minor post-6.19 merge conflict in drivers/gpu/drm/xe/xe_guc_log.c drivers/gpu/drm/xe/xe_guc_pc.c] Signed-off-by: Mingcong Bai [Xi Ruoyao: Fix a merge error.] Signed-off-by: Xi Ruoyao Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/gpu/drm/xe/xe_guc_ct.c ] Signed-off-by: Mingcong Bai --- drivers/gpu/drm/xe/xe_guc.c | 4 ++-- drivers/gpu/drm/xe/xe_guc_ads.c | 30 ++++++++++++++--------------- drivers/gpu/drm/xe/xe_guc_capture.c | 8 ++++---- drivers/gpu/drm/xe/xe_guc_ct.c | 4 ++-- drivers/gpu/drm/xe/xe_guc_pc.c | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index ccebb437e37fd0..14c52a24b2c40a 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -106,7 +106,7 @@ static u32 guc_ctl_feature_flags(struct xe_guc *guc) static u32 guc_ctl_log_params_flags(struct xe_guc *guc) { - u32 offset = guc_bo_ggtt_addr(guc, guc->log.bo) >> PAGE_SHIFT; + u32 offset = guc_bo_ggtt_addr(guc, guc->log.bo) >> XE_PTE_SHIFT; u32 flags; #if (((XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE) % SZ_1M) == 0) @@ -152,7 +152,7 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc) static u32 guc_ctl_ads_flags(struct xe_guc *guc) { - u32 ads = guc_bo_ggtt_addr(guc, guc->ads.bo) >> PAGE_SHIFT; + u32 ads = guc_bo_ggtt_addr(guc, guc->ads.bo) >> XE_PTE_SHIFT; u32 flags = FIELD_PREP(GUC_ADS_ADDR, ads); return flags; diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index 5760251cb685f6..ec834a7bca014d 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -142,17 +142,17 @@ static size_t guc_ads_regset_size(struct xe_guc_ads *ads) static size_t guc_ads_golden_lrc_size(struct xe_guc_ads *ads) { - return PAGE_ALIGN(ads->golden_lrc_size); + return ALIGN(ads->golden_lrc_size, SZ_4K); } static u32 guc_ads_waklv_size(struct xe_guc_ads *ads) { - return PAGE_ALIGN(ads->ads_waklv_size); + return ALIGN(ads->ads_waklv_size, SZ_4K); } static size_t guc_ads_capture_size(struct xe_guc_ads *ads) { - return PAGE_ALIGN(ads->capture_size); + return ALIGN(ads->capture_size, SZ_4K); } static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads) @@ -167,7 +167,7 @@ static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads) static size_t guc_ads_private_data_size(struct xe_guc_ads *ads) { - return PAGE_ALIGN(ads_to_guc(ads)->fw.private_data_size); + return ALIGN(ads_to_guc(ads)->fw.private_data_size, SZ_4K); } static size_t guc_ads_regset_offset(struct xe_guc_ads *ads) @@ -182,7 +182,7 @@ static size_t guc_ads_golden_lrc_offset(struct xe_guc_ads *ads) offset = guc_ads_regset_offset(ads) + guc_ads_regset_size(ads); - return PAGE_ALIGN(offset); + return ALIGN(offset, SZ_4K); } static size_t guc_ads_waklv_offset(struct xe_guc_ads *ads) @@ -192,7 +192,7 @@ static size_t guc_ads_waklv_offset(struct xe_guc_ads *ads) offset = guc_ads_golden_lrc_offset(ads) + guc_ads_golden_lrc_size(ads); - return PAGE_ALIGN(offset); + return ALIGN(offset, SZ_4K); } static size_t guc_ads_capture_offset(struct xe_guc_ads *ads) @@ -202,7 +202,7 @@ static size_t guc_ads_capture_offset(struct xe_guc_ads *ads) offset = guc_ads_waklv_offset(ads) + guc_ads_waklv_size(ads); - return PAGE_ALIGN(offset); + return ALIGN(offset, SZ_4K); } static size_t guc_ads_um_queues_offset(struct xe_guc_ads *ads) @@ -212,7 +212,7 @@ static size_t guc_ads_um_queues_offset(struct xe_guc_ads *ads) offset = guc_ads_capture_offset(ads) + guc_ads_capture_size(ads); - return PAGE_ALIGN(offset); + return ALIGN(offset, SZ_4K); } static size_t guc_ads_private_data_offset(struct xe_guc_ads *ads) @@ -222,7 +222,7 @@ static size_t guc_ads_private_data_offset(struct xe_guc_ads *ads) offset = guc_ads_um_queues_offset(ads) + guc_ads_um_queues_size(ads); - return PAGE_ALIGN(offset); + return ALIGN(offset, SZ_4K); } static size_t guc_ads_size(struct xe_guc_ads *ads) @@ -275,7 +275,7 @@ static size_t calculate_golden_lrc_size(struct xe_guc_ads *ads) continue; real_size = xe_gt_lrc_size(gt, class); - alloc_size = PAGE_ALIGN(real_size); + alloc_size = ALIGN(real_size, SZ_4K); total_size += alloc_size; } @@ -623,12 +623,12 @@ static int guc_capture_prep_lists(struct xe_guc_ads *ads) offsetof(struct __guc_ads_blob, system_info)); /* first, set aside the first page for a capture_list with zero descriptors */ - total_size = PAGE_SIZE; + total_size = SZ_4K; if (!xe_guc_capture_getnullheader(guc, &ptr, &size)) xe_map_memcpy_to(ads_to_xe(ads), ads_to_map(ads), capture_offset, ptr, size); null_ggtt = ads_ggtt + capture_offset; - capture_offset += PAGE_SIZE; + capture_offset += SZ_4K; /* * Populate capture list : at this point adps is already allocated and @@ -694,8 +694,8 @@ static int guc_capture_prep_lists(struct xe_guc_ads *ads) if (ads->capture_size != PAGE_ALIGN(total_size)) xe_gt_dbg(gt, "Updated ADS capture size %d (was %d)\n", - PAGE_ALIGN(total_size), ads->capture_size); - return PAGE_ALIGN(total_size); + ALIGN(total_size, SZ_4K), ads->capture_size); + return ALIGN(total_size, SZ_4K); } static void guc_mmio_regset_write_one(struct xe_guc_ads *ads, @@ -963,7 +963,7 @@ static void guc_golden_lrc_populate(struct xe_guc_ads *ads) xe_gt_assert(gt, gt->default_lrc[class]); real_size = xe_gt_lrc_size(gt, class); - alloc_size = PAGE_ALIGN(real_size); + alloc_size = ALIGN(real_size, SZ_4K); total_size += alloc_size; xe_map_memcpy_to(xe, ads_to_map(ads), offset, diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c index 2f5816c78fba21..0d67244895ece9 100644 --- a/drivers/gpu/drm/xe/xe_guc_capture.c +++ b/drivers/gpu/drm/xe/xe_guc_capture.c @@ -615,8 +615,8 @@ guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type, return -ENODATA; if (size) - *size = PAGE_ALIGN((sizeof(struct guc_debug_capture_list)) + - (num_regs * sizeof(struct guc_mmio_reg))); + *size = ALIGN((sizeof(struct guc_debug_capture_list)) + + (num_regs * sizeof(struct guc_mmio_reg)), SZ_4K); return 0; } @@ -763,7 +763,7 @@ size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc) * sequence, that is, during the pre-hwconfig phase before we have * the exact engine fusing info. */ - total_size = PAGE_SIZE; /* Pad a page in front for empty lists */ + total_size = SZ_4K; /* Pad a page in front for empty lists */ for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; i++) { for (j = 0; j < GUC_CAPTURE_LIST_CLASS_MAX; j++) { if (xe_guc_capture_getlistsize(guc, i, @@ -783,7 +783,7 @@ size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc) total_size += global_size; } - return PAGE_ALIGN(total_size); + return ALIGN(total_size, SZ_4K); } static int guc_capture_output_size_est(struct xe_guc *guc) diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c index a11cff7a20be7e..ea0e2d34e03efe 100644 --- a/drivers/gpu/drm/xe/xe_guc_ct.c +++ b/drivers/gpu/drm/xe/xe_guc_ct.c @@ -317,8 +317,8 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct) struct xe_gt *gt = ct_to_gt(ct); int err; - xe_gt_assert(gt, !(guc_h2g_size() % PAGE_SIZE)); - xe_gt_assert(gt, !(guc_g2h_size() % PAGE_SIZE)); + xe_gt_assert(gt, !(guc_h2g_size() % SZ_4K)); + xe_gt_assert(gt, !(guc_g2h_size() % SZ_4K)); err = drmm_mutex_init(&xe->drm, &ct->lock); if (err) diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c index bb8c4e7934926d..fa8fc261071c25 100644 --- a/drivers/gpu/drm/xe/xe_guc_pc.c +++ b/drivers/gpu/drm/xe/xe_guc_pc.c @@ -1221,7 +1221,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc) { struct xe_device *xe = pc_to_xe(pc); struct xe_gt *gt = pc_to_gt(pc); - u32 size = PAGE_ALIGN(sizeof(struct slpc_shared_data)); + u32 size = ALIGN(sizeof(struct slpc_shared_data), SZ_4K); ktime_t earlier; int ret; @@ -1331,7 +1331,7 @@ int xe_guc_pc_init(struct xe_guc_pc *pc) struct xe_tile *tile = gt_to_tile(gt); struct xe_device *xe = gt_to_xe(gt); struct xe_bo *bo; - u32 size = PAGE_ALIGN(sizeof(struct slpc_shared_data)); + u32 size = ALIGN(sizeof(struct slpc_shared_data), SZ_4K); int err; if (xe->info.skip_guc_pc) From dce06ff7696c5073ceefd659bf40a4d7232b4841 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Feb 2025 10:00:20 +0800 Subject: [PATCH 122/521] BACKPORT: FROMLIST: drm/xe/regs: fix RING_CTL_SIZE(size) calculation Similar to the preceding patch for GuC (and with the same references), Intel DG1 and DG2 GPUs expects command buffers to align to 4K boundaries. Current code uses `PAGE_SIZE' as an assumed alignment reference but 4K kernel page sizes is by no means a guarantee. On 16K-paged kernels, this causes driver failures during boot up: [ 14.018975] ------------[ cut here ]------------ [ 14.023562] xe 0000:09:00.0: [drm] GT0: Kernel-submitted job timed out [ 14.030084] WARNING: CPU: 3 PID: 564 at drivers/gpu/drm/xe/xe_guc_submit.c:1181 guc_exec_queue_timedout_job+0x1c0/0xacc [xe] [ 14.041300] Modules linked in: nf_conntrack_netbios_ns(E) nf_conntrack_broadcast(E) nft_fib_inet(E) nft_fib_ipv4(E) nft_fib_ipv6(E) nft_fib(E) nft_reject_inet(E) nf_reject_ipv4(E) nf_reject_ipv6(E) nft_reject(E) nft_ct(E) nft_chain_nat(E) ip6table_nat(E) ip6table_mangle(E) ip6table_raw(E) ip6table_security(E) iptable_nat(E) nf_nat(E) nf_conntrack(E) nf_defrag_ipv6(E) nf_defrag_ipv4(E) rfkill(E) iptable_mangle(E) iptable_raw(E) iptable_security(E) ip_set(E) nf_tables(E) ip6table_filter(E) ip6_tables(E) iptable_filter(E) snd_hda_codec_conexant(E) snd_hda_codec_generic(E) snd_hda_codec_hdmi(E) nls_iso8859_1(E) snd_hda_intel(E) snd_intel_dspcfg(E) qrtr(E) nls_cp437(E) snd_hda_codec(E) spi_loongson_pci(E) rtc_efi(E) snd_hda_core(E) loongson3_cpufreq(E) spi_loongson_core(E) snd_hwdep(E) snd_pcm(E) snd_timer(E) snd(E) soundcore(E) gpio_loongson_64bit(E) input_leds(E) rtc_loongson(E) i2c_ls2x(E) mousedev(E) sch_fq_codel(E) fuse(E) nfnetlink(E) dmi_sysfs(E) ip_tables(E) x_tables(E) xe(E) d rm_gpuvm(E) drm_buddy(E) gpu_sched(E) [ 14.041369] drm_exec(E) drm_suballoc_helper(E) drm_display_helper(E) cec(E) rc_core(E) hid_generic(E) tpm_tis_spi(E) r8169(E) realtek(E) led_class(E) loongson(E) i2c_algo_bit(E) drm_ttm_helper(E) ttm(E) drm_client_lib(E) drm_kms_helper(E) sunrpc(E) i2c_dev(E) [ 14.153910] CPU: 3 UID: 0 PID: 564 Comm: kworker/u32:2 Tainted: G E 6.14.0-rc4-aosc-main-gbad70b1cd8b0-dirty #7 [ 14.165325] Tainted: [E]=UNSIGNED_MODULE [ 14.169220] Hardware name: Loongson Loongson-3A6000-HV-7A2000-1w-V0.1-EVB/Loongson-3A6000-HV-7A2000-1w-EVB-V1.21, BIOS Loongson-UDK2018-V4.0.05756-prestab [ 14.182970] Workqueue: gt-ordered-wq drm_sched_job_timedout [gpu_sched] [ 14.189549] pc ffff8000024f3760 ra ffff8000024f3760 tp 900000012f150000 sp 900000012f153ca0 [ 14.197853] a0 0000000000000000 a1 0000000000000000 a2 0000000000000000 a3 0000000000000000 [ 14.206156] a4 0000000000000000 a5 0000000000000000 a6 0000000000000000 a7 0000000000000000 [ 14.214458] t0 0000000000000000 t1 0000000000000000 t2 0000000000000000 t3 0000000000000000 [ 14.222761] t4 0000000000000000 t5 0000000000000000 t6 0000000000000000 t7 0000000000000000 [ 14.231064] t8 0000000000000000 u0 900000000195c0c8 s9 900000012e4dcf48 s0 90000001285f3640 [ 14.239368] s1 90000001004f8000 s2 ffff8000026ec000 s3 0000000000000000 s4 900000012e4dc028 [ 14.247672] s5 90000001009f5e00 s6 000000000000137e s7 0000000000000001 s8 900000012f153ce8 [ 14.255975] ra: ffff8000024f3760 guc_exec_queue_timedout_job+0x1c0/0xacc [xe] [ 14.263379] ERA: ffff8000024f3760 guc_exec_queue_timedout_job+0x1c0/0xacc [xe] [ 14.270777] CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE) [ 14.276927] PRMD: 00000004 (PPLV0 +PIE -PWE) [ 14.281258] EUEN: 00000000 (-FPE -SXE -ASXE -BTE) [ 14.286024] ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7) [ 14.290790] ESTAT: 000c0000 [BRK] (IS= ECode=12 EsubCode=0) [ 14.296329] PRID: 0014d000 (Loongson-64bit, Loongson-3A6000-HV) [ 14.302299] CPU: 3 UID: 0 PID: 564 Comm: kworker/u32:2 Tainted: G E 6.14.0-rc4-aosc-main-gbad70b1cd8b0-dirty #7 [ 14.302302] Tainted: [E]=UNSIGNED_MODULE [ 14.302302] Hardware name: Loongson Loongson-3A6000-HV-7A2000-1w-V0.1-EVB/Loongson-3A6000-HV-7A2000-1w-EVB-V1.21, BIOS Loongson-UDK2018-V4.0.05756-prestab [ 14.302304] Workqueue: gt-ordered-wq drm_sched_job_timedout [gpu_sched] [ 14.302307] Stack : 900000012f153928 d84a6232d48f1ac7 900000000023eb34 900000012f150000 [ 14.302310] 900000012f153900 0000000000000000 900000012f153908 9000000001c31c70 [ 14.302313] 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 14.302315] 0000000000000000 d84a6232d48f1ac7 0000000000000000 0000000000000000 [ 14.302318] 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 14.302320] 0000000000000000 0000000000000000 00000000072b4000 900000012e4dcf48 [ 14.302323] 9000000001eb8000 0000000000000000 9000000001c31c70 0000000000000004 [ 14.302325] 0000000000000004 0000000000000000 000000000000137e 0000000000000001 [ 14.302328] 900000012f153ce8 9000000001c31c70 9000000000244174 0000555581840b98 [ 14.302331] 00000000000000b0 0000000000000004 0000000000000000 0000000000071c1d [ 14.302333] ... [ 14.302335] Call Trace: [ 14.302336] [<9000000000244174>] show_stack+0x3c/0x16c [ 14.302341] [<900000000023eb30>] dump_stack_lvl+0x84/0xe0 [ 14.302346] [<9000000000288208>] __warn+0x8c/0x174 [ 14.302350] [<90000000017c1918>] report_bug+0x1c0/0x22c [ 14.302354] [<90000000017f66e8>] do_bp+0x280/0x344 [ 14.302359] [ 14.302360] ---[ end trace 0000000000000000 ]--- Revise calculation of `RING_CTL_SIZE(size)' to use `SZ_4K' to fix the aforementioned issue. Cc: stable@vger.kernel.org Fixes: b79e8fd954c4 ("drm/xe: Remove dependency on intel_engine_regs.h") Tested-by: Mingcong Bai Tested-by: Haien Liang <27873200@qq.com> Tested-by: Shirong Liu Tested-by: Haofeng Wu Link: https://github.com/FanFansfan/loongson-linux/commit/22c55ab3931c32410a077b3ddb6dca3f28223360 Co-developed-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Mingcong Bai Reviewed-by: Matthew Brost [Mingcong Bai: Resolved a minor merge conflict post-6.16 in drivers/gpu/drm/xe/regs/xe_engine_regs.h] Link: https://lore.kernel.org/all/20250613-upstream-xe-non-4k-v2-v2-1-934f82249f8a@aosc.io/ Signed-off-by: Mingcong Bai --- drivers/gpu/drm/xe/regs/xe_engine_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h index 1b4a7e9a703df7..aa7160d6fcac48 100644 --- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h @@ -56,7 +56,7 @@ #define RING_START(base) XE_REG((base) + 0x38) #define RING_CTL(base) XE_REG((base) + 0x3c) -#define RING_CTL_SIZE(size) ((size) - PAGE_SIZE) /* in bytes -> pages */ +#define RING_CTL_SIZE(size) ((size) - SZ_4K) /* in bytes -> pages */ #define RING_START_UDW(base) XE_REG((base) + 0x48) From b7175c9294cb1165fac8c40b81341aa9ed9bcd8e Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Feb 2025 10:00:21 +0800 Subject: [PATCH 123/521] FROMLIST: drm/xe: use 4K alignment for cursor jumps It appears that the xe_res_cursor also assumes 4K alignment. Current code uses `PAGE_SIZE' as an assumed alignment reference but 4K kernel page sizes is by no means a guarantee. On 16K-paged kernels, this causes driver failures during boot up: [ 23.242757] ------------[ cut here ]------------ [ 23.247363] WARNING: CPU: 0 PID: 2036 at drivers/gpu/drm/xe/xe_res_cursor.h:182 emit_pte+0x394/0x3b0 [xe] [ 23.256962] Modules linked in: nf_conntrack_netbios_ns(E) nf_conntrack_broadcast(E) nft_fib_inet(E) nft_fib_ipv4(E) nft_fib_ipv6(E) nft_fib(E) nft_reject_inet(E) nf_reject_ipv4(E) nf_reject_ipv6(E) nft_reject(E) nft_ct(E) rfkill(E) nft_chain_nat(E) ip6table_nat(E) ip6table_mangle(E) ip6table_raw(E) ip6table_security(E) iptable_nat(E) nf_nat(E) nf_conntrack(E) nf_defrag_ipv6(E) nf_defrag_ipv4(E) iptable_mangle(E) iptable_raw(E) iptable_security(E) ip_set(E) nf_tables(E) ip6table_filter(E) ip6_tables(E) iptable_filter(E) snd_hda_codec_conexant(E) snd_hda_codec_generic(E) snd_hda_codec_hdmi(E) snd_hda_intel(E) snd_intel_dspcfg(E) snd_hda_codec(E) nls_iso8859_1(E) qrtr(E) nls_cp437(E) snd_hda_core(E) loongson3_cpufreq(E) rtc_efi(E) snd_hwdep(E) snd_pcm(E) spi_loongson_pci(E) snd_timer(E) snd(E) spi_loongson_core(E) soundcore(E) gpio_loongson_64bit(E) rtc_loongson(E) i2c_ls2x(E) mousedev(E) input_leds(E) sch_fq_codel(E) fuse(E) nfnetlink(E) dmi_sysfs(E) ip_tables(E) x_tables(E) xe(E) d rm_gpuvm(E) drm_buddy(E) gpu_sched(E) [ 23.257034] drm_exec(E) drm_suballoc_helper(E) drm_display_helper(E) cec(E) rc_core(E) hid_generic(E) tpm_tis_spi(E) r8169(E) loongson(E) i2c_algo_bit(E) realtek(E) drm_ttm_helper(E) led_class(E) ttm(E) drm_client_lib(E) drm_kms_helper(E) sunrpc(E) i2c_dev(E) [ 23.369697] CPU: 0 UID: 1000 PID: 2036 Comm: QSGRenderThread Tainted: G E 6.14.0-rc4-aosc-main-g7cc07e6e50b0-dirty #8 [ 23.381640] Tainted: [E]=UNSIGNED_MODULE [ 23.385534] Hardware name: Loongson Loongson-3A6000-HV-7A2000-1w-V0.1-EVB/Loongson-3A6000-HV-7A2000-1w-EVB-V1.21, BIOS Loongson-UDK2018-V4.0.05756-prestab [ 23.399319] pc ffff80000251efc0 ra ffff80000251eddc tp 900000011fe3c000 sp 900000011fe3f7e0 [ 23.407632] a0 0000000000000001 a1 0000000000000000 a2 0000000000000000 a3 0000000000000000 [ 23.415938] a4 0000000000000000 a5 0000000000000000 a6 0000000000060000 a7 900000010c947b00 [ 23.424240] t0 0000000000000000 t1 0000000000000000 t2 0000000000000000 t3 900000012e456230 [ 23.432543] t4 0000000000000035 t5 0000000000004000 t6 00000001fbc40403 t7 0000000000004000 [ 23.440845] t8 9000000100e688a8 u0 5cc06cee8ef0edee s9 9000000100024420 s0 0000000000000047 [ 23.449147] s1 0000000000004000 s2 0000000000000001 s3 900000012adba000 s4 ffffffffffffc000 [ 23.457450] s5 9000000108939428 s6 0000000000000000 s7 0000000000000000 s8 900000011fe3f8e0 [ 23.465851] ra: ffff80000251eddc emit_pte+0x1b0/0x3b0 [xe] [ 23.471761] ERA: ffff80000251efc0 emit_pte+0x394/0x3b0 [xe] [ 23.477557] CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE) [ 23.483732] PRMD: 00000004 (PPLV0 +PIE -PWE) [ 23.488068] EUEN: 00000003 (+FPE +SXE -ASXE -BTE) [ 23.492832] ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7) [ 23.497594] ESTAT: 000c0000 [BRK] (IS= ECode=12 EsubCode=0) [ 23.503133] PRID: 0014d000 (Loongson-64bit, Loongson-3A6000-HV) [ 23.509164] CPU: 0 UID: 1000 PID: 2036 Comm: QSGRenderThread Tainted: G E 6.14.0-rc4-aosc-main-g7cc07e6e50b0-dirty #8 [ 23.509168] Tainted: [E]=UNSIGNED_MODULE [ 23.509168] Hardware name: Loongson Loongson-3A6000-HV-7A2000-1w-V0.1-EVB/Loongson-3A6000-HV-7A2000-1w-EVB-V1.21, BIOS Loongson-UDK2018-V4.0.05756-prestab [ 23.509170] Stack : ffffffffffffffff ffffffffffffffff 900000000023eb34 900000011fe3c000 [ 23.509176] 900000011fe3f440 0000000000000000 900000011fe3f448 9000000001c31c70 [ 23.509181] 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 23.509185] 0000000000000000 5cc06cee8ef0edee 0000000000000000 0000000000000000 [ 23.509190] 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 23.509193] 0000000000000000 0000000000000000 00000000066b4000 9000000100024420 [ 23.509197] 9000000001eb8000 0000000000000000 9000000001c31c70 0000000000000004 [ 23.509202] 0000000000000004 0000000000000000 0000000000000000 0000000000000000 [ 23.509206] 900000011fe3f8e0 9000000001c31c70 9000000000244174 00007fffac097534 [ 23.509211] 00000000000000b0 0000000000000004 0000000000000003 0000000000071c1d [ 23.509216] ... [ 23.509218] Call Trace: [ 23.509220] [<9000000000244174>] show_stack+0x3c/0x16c [ 23.509226] [<900000000023eb30>] dump_stack_lvl+0x84/0xe0 [ 23.509230] [<9000000000288208>] __warn+0x8c/0x174 [ 23.509234] [<90000000017c1918>] report_bug+0x1c0/0x22c [ 23.509238] [<90000000017f66e8>] do_bp+0x280/0x344 [ 23.509243] [<90000000002428a0>] handle_bp+0x120/0x1c0 [ 23.509247] [] emit_pte+0x394/0x3b0 [xe] [ 23.509295] [] xe_migrate_clear+0x2d8/0xa54 [xe] [ 23.509341] [] xe_bo_move+0x324/0x930 [xe] [ 23.509387] [] ttm_bo_handle_move_mem+0xd0/0x194 [ttm] [ 23.509392] [] ttm_bo_validate+0xd4/0x1cc [ttm] [ 23.509396] [] ttm_bo_init_reserved+0x184/0x1dc [ttm] [ 23.509399] [] ___xe_bo_create_locked+0x1e8/0x3d4 [xe] [ 23.509445] [] __xe_bo_create_locked+0x2cc/0x390 [xe] [ 23.509489] [] xe_bo_create_user+0x34/0xe4 [xe] [ 23.509533] [] xe_gem_create_ioctl+0x154/0x4d8 [xe] [ 23.509578] [<9000000001062784>] drm_ioctl_kernel+0xe0/0x14c [ 23.509582] [<9000000001062c10>] drm_ioctl+0x420/0x5f4 [ 23.509585] [] xe_drm_ioctl+0x64/0xac [xe] [ 23.509630] [<9000000000653504>] sys_ioctl+0x2b8/0xf98 [ 23.509634] [<90000000017f684c>] do_syscall+0xa0/0x140 [ 23.509637] [<9000000000241e38>] handle_syscall+0xb8/0x158 [ 23.509640] [ 23.509644] ---[ end trace 0000000000000000 ]--- Revise calls to `xe_res_dma()' and `xe_res_cursor()' to use `XE_PTE_MASK' (12) and `SZ_4K' to fix this potentially confused use of `PAGE_SIZE' in relevant code. Cc: stable@vger.kernel.org Fixes: e89b384cde62 ("drm/xe/migrate: Update emit_pte to cope with a size level than 4k") Tested-by: Mingcong Bai Tested-by: Haien Liang <27873200@qq.com> Tested-by: Shirong Liu Tested-by: Haofeng Wu Link: https://github.com/FanFansfan/loongson-linux/commit/22c55ab3931c32410a077b3ddb6dca3f28223360 Co-developed-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Mingcong Bai Link: https://lore.kernel.org/all/20250613-upstream-xe-non-4k-v2-v2-1-934f82249f8a@aosc.io/ Signed-off-by: Mingcong Bai --- drivers/gpu/drm/xe/xe_migrate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index a22413f892a096..e5714c85344757 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -659,7 +659,7 @@ static void emit_pte(struct xe_migrate *m, u64 addr, flags = 0; bool devmem = false; - addr = xe_res_dma(cur) & PAGE_MASK; + addr = xe_res_dma(cur) & ~XE_PTE_MASK; if (is_vram) { if (vm->flags & XE_VM_FLAG_64K) { u64 va = cur_ofs * XE_PAGE_SIZE / 8; @@ -680,7 +680,7 @@ static void emit_pte(struct xe_migrate *m, bb->cs[bb->len++] = lower_32_bits(addr); bb->cs[bb->len++] = upper_32_bits(addr); - xe_res_next(cur, min_t(u32, size, PAGE_SIZE)); + xe_res_next(cur, min_t(u32, size, SZ_4K)); cur_ofs += 8; } } From c9ed5be17bd63145a3a0dea956fa25ecf3476291 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Feb 2025 10:00:22 +0800 Subject: [PATCH 124/521] FROMLIST: drm/xe/query: use PAGE_SIZE as the minimum page alignment As this component hooks into userspace API, it should be assumed that it will play well with non-4K/64K pages. Use `PAGE_SIZE' as the final reference for page alignment instead. Cc: stable@vger.kernel.org Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Fixes: 801989b08aff ("drm/xe/uapi: Make constant comments visible in kernel doc") Tested-by: Mingcong Bai Tested-by: Haien Liang <27873200@qq.com> Tested-by: Shirong Liu Tested-by: Haofeng Wu Link: https://github.com/FanFansfan/loongson-linux/commit/22c55ab3931c32410a077b3ddb6dca3f28223360 Co-developed-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Shang Yatsen <429839446@qq.com> Signed-off-by: Mingcong Bai Link: https://lore.kernel.org/all/20250613-upstream-xe-non-4k-v2-v2-1-934f82249f8a@aosc.io/ Signed-off-by: Mingcong Bai --- drivers/gpu/drm/xe/xe_query.c | 2 +- include/uapi/drm/xe_drm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c index d84d6a422c45d6..4c47e88dce0e58 100644 --- a/drivers/gpu/drm/xe/xe_query.c +++ b/drivers/gpu/drm/xe/xe_query.c @@ -345,7 +345,7 @@ static int query_config(struct xe_device *xe, struct drm_xe_device_query *query) config->info[DRM_XE_QUERY_CONFIG_FLAGS] |= DRM_XE_QUERY_CONFIG_FLAG_HAS_PURGING_SUPPORT; config->info[DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT] = - xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K; + xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : PAGE_SIZE; config->info[DRM_XE_QUERY_CONFIG_VA_BITS] = xe->info.va_bits; config->info[DRM_XE_QUERY_CONFIG_MAX_EXEC_QUEUE_PRIORITY] = xe_exec_queue_device_get_max_priority(xe); diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index ae2fda23ce7c7c..642caac28544ae 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -413,7 +413,7 @@ struct drm_xe_query_mem_regions { * if a queue can be creaed with * %DRM_XE_EXEC_QUEUE_SET_DISABLE_STATE_CACHE_PERF_FIX * - %DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT - Minimal memory alignment - * required by this device, typically SZ_4K or SZ_64K + * required by this device, typically PAGE_SIZE. * - %DRM_XE_QUERY_CONFIG_VA_BITS - Maximum bits of a virtual address * - %DRM_XE_QUERY_CONFIG_MAX_EXEC_QUEUE_PRIORITY - Value of the highest * available exec queue priority From 8129ea7c3e60aabb14e40cbc9064ea30627505de Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 14 Aug 2025 11:21:36 +0800 Subject: [PATCH 125/521] FROMLIST: RFC: drm/amdkfd: disable HSA_AMD_SVM on LoongArch and AArch64 While testing my ROCm port for LoongArch and AArch64 (patches pending) on the following platforms: - LoongArch ... - Loongson AC612A0_V1.1 (Loongson 3C6000/S) + AMD Radeon RX 6800 - AArch64 ... - FD30M51 (Phytium FT-D3000) + AMD Radeon RX 7600 - Huawei D920S10 (Huawei Kunpeng 920) + AMD Radeon RX 7600 When HSA_AMD_SVM is enabled, amdgpu would fail to initialise at all on LoongArch (no output): amdgpu 0000:0d:00.0: amdgpu: kiq ring mec 2 pipe 1 q 0 CPU 0 Unable to handle kernel paging request at virtual address ffffffffff800034, era == 9000000001058044, ra == 9000000001058660 Oops[#1]: CPU: 0 UID: 0 PID: 202 Comm: kworker/0:3 Not tainted 6.16.0+ #103 PREEMPT(full) Hardware name: To be filled by O.E.M.To be fill To be filled by O.E.M.To be fill/To be filled by O.E.M.To be fill, BIOS Loongson-UDK2018-V4.0. Workqueue: events work_for_cpu_fn pc 9000000001058044 ra 9000000001058660 tp 9000000101500000 sp 9000000101503aa0 a0 ffffffffff800000 a1 0000000ffffe0000 a2 0000000000000000 a3 90000001207c58e0 a4 9000000001a4c310 a5 0000000000000001 a6 0000000000000000 a7 0000000000000001 t0 000003ffff800000 t1 0000000000000001 t2 0000040000000000 t3 03ffff0000002000 t4 0000000000000000 t5 0001010101010101 t6 ffff800000000000 t7 0001000000000000 t8 000000000000002f u0 0000000000800000 s9 9000000002026000 s0 90000001207c58e0 s1 0000000000000001 s2 9000000001935c40 s3 0000001000000000 s4 0000000000000001 s5 0000000ffffe0000 s6 0000000000000040 s7 0001000000000001 s8 0001000000000000 ra: 9000000001058660 memmap_init_zone_device+0x120/0x1b0 ERA: 9000000001058044 __init_zone_device_page.constprop.0+0x4/0x1a0 CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE) PRMD: 00000004 (PPLV0 +PIE -PWE) EUEN: 00000000 (-FPE -SXE -ASXE -BTE) ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7) ESTAT: 00020000 [PIS] (IS= ECode=2 EsubCode=0) BADV: ffffffffff800034 PRID: 0014d010 (Loongson-64bit, Loongson-3C6000/S) Modules linked in: amdgpu(+) vfat fat cfg80211 rfkill 8021q garp stp mrp llc snd_hda_codec_atihdmi snd_hda_codec_hdmi snd_hda_codec_conexant snd_hda_codec_generic drm_client_lib drm_ttm_helper syscopyarea ttm sysfillrect sysimgblt fb_sys_fops drm_panel_backlight_quirks video drm_exec drm_suballoc_helper amdxcp mfd_core drm_buddy gpu_sched drm_display_helper drm_kms_helper cec snd_hda_intel ipmi_ssif snd_intel_dspcfg snd_hda_codec snd_hda_core acpi_ipmi snd_hwdep snd_pcm fb loongson3_cpufreq lcd igc snd_timer ipmi_si spi_loongson_pci spi_loongson_core snd ipmi_devintf soundcore ipmi_msghandler binfmt_misc fuse drm drm_panel_orientation_quirks backlight dm_mod dax nfnetlink Process kworker/0:3 (pid: 202, threadinfo=00000000eb7cd5d6, task=000000004ca22b1b) Stack : 0000000000001440 0000000000000000 ffffffffff800000 0000000000000001 90000000020b5978 9000000101503b38 0000000000000001 0000000000000001 0000000000000000 90000000020b5978 90000000020b3f48 0000000000001440 0000000000000000 90000001207c58e0 90000001207c5970 9000000000575e20 90000000010e2e00 90000000020b3f48 900000000205c238 0000000000000000 00000000000001d3 90000001207c58e0 9000000001958f28 9000000120790848 90000001207b3510 0000000000000000 9000000120780000 9000000120780010 90000001207d6000 90000001207c58e0 90000001015660c8 9000000120780000 0000000000000000 90000000005763a8 90000001207c58e0 00000003ff000000 9000000120780000 ffff80000296b820 900000012078f968 90000001207c6000 ... Call Trace: [<9000000001058044>] __init_zone_device_page.constprop.0+0x4/0x1a0 [<900000000105865c>] memmap_init_zone_device+0x11c/0x1b0 [<9000000000575e1c>] memremap_pages+0x24c/0x7b0 [<90000000005763a4>] devm_memremap_pages+0x24/0x80 [] kgd2kfd_init_zone_device+0x11c/0x220 [amdgpu] [] amdgpu_device_init+0x27dc/0x2bf0 [amdgpu] [] amdgpu_driver_load_kms+0x18/0x90 [amdgpu] [] amdgpu_pci_probe+0x22c/0x890 [amdgpu] [<9000000000916adc>] local_pci_probe+0x3c/0xb0 [<90000000002976c8>] work_for_cpu_fn+0x18/0x30 [<900000000029aeb4>] process_one_work+0x164/0x320 [<900000000029b96c>] worker_thread+0x37c/0x4a0 [<90000000002a695c>] kthread+0x12c/0x220 [<9000000001055b64>] ret_from_kernel_thread+0x24/0xc0 [<9000000000237524>] ret_from_kernel_thread_asm+0xc/0x88 Code: 00000000 00000000 0280040d <2980d08d> 02bffc0e 2980c08e 02c0208d 29c0208d 1400004f ---[ end trace 0000000000000000 ]--- Or lock up and/or driver reset during computate tasks, such as when running llama.cpp over ROCm, at which point the compute process must be killed before the reset could complete: amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE amdgpu 0000:0a:00.0: amdgpu: failed to remove hardware queue from MES, doorbell=0x1202 amdgpu 0000:0a:00.0: amdgpu: MES might be in unrecoverable state, issue a GPU reset amdgpu 0000:0a:00.0: amdgpu: Failed to evict queue 3 amdgpu 0000:0a:00.0: amdgpu: GPU reset begin! amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE amdgpu 0000:0a:00.0: amdgpu: failed to remove hardware queue from MES, doorbell=0x1004 amdgpu 0000:0a:00.0: amdgpu: MES might be in unrecoverable state, issue a GPU reset amdgpu 0000:0a:00.0: amdgpu: Failed to evict queue 2 amdgpu 0000:0a:00.0: amdgpu: Failed to evict queue 1 amdgpu 0000:0a:00.0: amdgpu: Failed to evict queue 0 amdgpu: Failed to quiesce KFD amdgpu 0000:0a:00.0: amdgpu: Dumping IP State amdgpu 0000:0a:00.0: amdgpu: Dumping IP State Completed amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MES failed to respond to msg=REMOVE_QUEUE [drm:amdgpu_mes_unmap_legacy_queue [amdgpu]] *ERROR* failed to unmap legacy queue amdgpu 0000:0a:00.0: amdgpu: MODE1 reset amdgpu 0000:0a:00.0: amdgpu: GPU mode1 reset amdgpu 0000:0a:00.0: amdgpu: GPU smu mode1 reset amdgpu 0000:0a:00.0: amdgpu: GPU reset succeeded, trying to resume Disabling the aforementioned option makes the issue go away, though it is unclear whether this is a platform-specific issue or one that lies within the amdkfd code. This patch has been tested on all the aforementioned platform combinations, and sent as an RFC to encourage discussion. Signed-off-by: Zhang Yuhao Signed-off-by: Mingcong Bai Tested-by: Mingcong Bai Link: https://lore.kernel.org/all/20250814032153.227285-1-jeffbai@aosc.io/ Signed-off-by: Mingcong Bai --- drivers/gpu/drm/amd/amdkfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/Kconfig b/drivers/gpu/drm/amd/amdkfd/Kconfig index a5d7467c2f3493..1c9e9529ca181b 100644 --- a/drivers/gpu/drm/amd/amdkfd/Kconfig +++ b/drivers/gpu/drm/amd/amdkfd/Kconfig @@ -14,7 +14,7 @@ config HSA_AMD config HSA_AMD_SVM bool "Enable HMM-based shared virtual memory manager" - depends on HSA_AMD && DEVICE_PRIVATE + depends on HSA_AMD && DEVICE_PRIVATE && !LOONGARCH && !ARM64 default y select HMM_MIRROR select MMU_NOTIFIER From 41e6c0421902ebf249e214756fc0eafa6697f28f Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 29 Jun 2024 13:22:46 +0800 Subject: [PATCH 126/521] BACKPORT: FROMLIST: drm/ttm: save the device's DMA coherency status in ttm_device Currently TTM utilizes cached memory regardless of whether the device have full DMA coherency (can snoop CPU cache). Save the device's DMA coherency status in struct ttm_device, to allow further support of devices w/o snooping capability (the capability missing on at least one part of the transmission between the CPU and the device). Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20240629052247.2653363-2-uwu@icenowy.me [ Han Gao: add conditional compilation for dma_coherent ] Signed-off-by: Han Gao --- drivers/gpu/drm/ttm/ttm_device.c | 6 ++++++ include/drm/ttm/ttm_device.h | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index d3bfb9a696a741..7fad8dcd1ecfe3 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -244,6 +244,12 @@ int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *func list_add_tail(&bdev->device_list, &glob->device_list); mutex_unlock(&ttm_global_mutex); +#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) + bdev->dma_coherent = dev->dma_coherent; +#endif + return 0; } EXPORT_SYMBOL(ttm_device_init); diff --git a/include/drm/ttm/ttm_device.h b/include/drm/ttm/ttm_device.h index 5618aef462f21b..5b7c6a7a0e3b34 100644 --- a/include/drm/ttm/ttm_device.h +++ b/include/drm/ttm/ttm_device.h @@ -231,6 +231,15 @@ struct ttm_device { */ const struct ttm_device_funcs *funcs; +#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) + /** + * @dma_coherent: if the device backed is dma-coherent. + */ + bool dma_coherent; +#endif + /** * @sysman: Resource manager for the system domain. * Access via ttm_manager_type. From 574ad76169eecb7d5a7497ca846f1eed935b33c9 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 29 Jun 2024 13:22:47 +0800 Subject: [PATCH 127/521] BACKPORT: FROMLIST: drm/ttm: downgrade cached to write_combined when snooping not available As we can now acquire the presence of the full DMA coherency (snooping capability) from ttm_device, we can now map the CPU side memory as write-combined when cached is requested and snooping is not avilable. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20240629052247.2653363-3-uwu@icenowy.me [ Han Gao: add conditional compilation for dma coherent operations ] [ Icenowy: omit single page ttm_cached optimization when non-coherent and handle linear io iter ] Signed-off-by: Han Gao --- drivers/gpu/drm/ttm/ttm_bo_util.c | 13 +++++++++++++ drivers/gpu/drm/ttm/ttm_resource.c | 13 +++++++++++-- drivers/gpu/drm/ttm/ttm_tt.c | 8 ++++++++ include/drm/ttm/ttm_caching.h | 3 ++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 3e3c201a022267..6f33c77d47159a 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -307,6 +307,14 @@ pgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res, caching = res->bus.caching; } +#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) + /* Downgrade cached mapping for non-snooping devices */ + if (!bo->bdev->dma_coherent && caching == ttm_cached) + caching = ttm_write_combined; +#endif + return ttm_prot_from_caching(caching, tmp); } EXPORT_SYMBOL(ttm_io_prot); @@ -357,6 +365,11 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, return ret; if (num_pages == 1 && ttm->caching == ttm_cached && +#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) + bo->bdev->dma_coherent && +#endif !(man->use_tt && (ttm->page_flags & TTM_TT_FLAG_DECRYPTED))) { /* * We're mapping a single page, and the desired diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 154d6739256f86..9ba711790a9528 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -848,8 +848,17 @@ ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io, struct ttm_device *bdev, struct ttm_resource *mem) { + enum ttm_caching caching = mem->bus.caching; int ret; +#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) + /* Downgrade cached mapping for non-snooping devices */ + if (!bdev->dma_coherent && caching == ttm_cached) + caching = ttm_write_combined; +#endif + ret = ttm_mem_io_reserve(bdev, mem); if (ret) goto out_err; @@ -864,11 +873,11 @@ ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io, } else { iter_io->needs_unmap = true; memset(&iter_io->dmap, 0, sizeof(iter_io->dmap)); - if (mem->bus.caching == ttm_write_combined) + if (caching == ttm_write_combined) iosys_map_set_vaddr_iomem(&iter_io->dmap, ioremap_wc(mem->bus.offset, mem->size)); - else if (mem->bus.caching == ttm_cached) + else if (caching == ttm_cached) iosys_map_set_vaddr(&iter_io->dmap, memremap(mem->bus.offset, mem->size, MEMREMAP_WB | diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index b645a181818431..47244d1c1b7aeb 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -156,6 +156,14 @@ static void ttm_tt_init_fields(struct ttm_tt *ttm, enum ttm_caching caching, unsigned long extra_pages) { +#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) + /* Downgrade cached mapping for non-snooping devices */ + if (!bo->bdev->dma_coherent && caching == ttm_cached) + caching = ttm_write_combined; +#endif + ttm->num_pages = (PAGE_ALIGN(bo->base.size) >> PAGE_SHIFT) + extra_pages; ttm->page_flags = page_flags; ttm->dma_address = NULL; diff --git a/include/drm/ttm/ttm_caching.h b/include/drm/ttm/ttm_caching.h index a18f43e93abab4..f92d7911f50e4d 100644 --- a/include/drm/ttm/ttm_caching.h +++ b/include/drm/ttm/ttm_caching.h @@ -47,7 +47,8 @@ enum ttm_caching { /** * @ttm_cached: Fully cached like normal system memory, requires that - * devices snoop the CPU cache on accesses. + * devices snoop the CPU cache on accesses. Downgraded to + * ttm_write_combined when the snooping capaiblity is missing. */ ttm_cached }; From 07405d38fea23c54a0efc576eea9bd8712897565 Mon Sep 17 00:00:00 2001 From: "Guo Ren (Alibaba DAMO Academy)" Date: Tue, 21 Apr 2026 10:31:40 -0400 Subject: [PATCH 128/521] FROMLIST: riscv: errata: Add ERRATA_THEAD_WRITE_ONCE fixup The early version of XuanTie C910 core has a store merge buffer delay problem. The store merge buffer could improve the store queue performance by merging multi-store requests, but when there are not continued store requests, the prior single store request would be waiting in the store queue for a long time. That would cause significant problems for communication between multi-cores. This problem was found on sg2042 & th1520 platforms with the qspinlock lock torture test. So appending a fence w.o could immediately flush the store merge buffer and let other cores see the write result. This will apply the WRITE_ONCE errata to handle the non-standard behavior via appending a fence w.o instruction for WRITE_ONCE(). This problem is only observed on the sg2042 hardware platform by running the lock_torture test program for half an hour. The problem was not found in the user space application, because interrupt can break the livelock. Acked-by: Arnd Bergmann Reviewed-by: Alexandre Ghiti Reviewed-by: Leonardo Bras Reviewed-by: Inochi Amaoto Tested-by: Han Gao Tested-by: Yao Zi Cc: Chen Wang Cc: Xiaoguang Xing Cc: Paul Walmsley Signed-off-by: Guo Ren (Alibaba DAMO Academy) Link: https://lore.kernel.org/all/20260421143154.1590156-1-guoren@kernel.org/ Signed-off-by: Mingcong Bai --- arch/riscv/Kconfig.errata | 17 ++++++++++ arch/riscv/errata/thead/errata.c | 20 ++++++++++++ arch/riscv/include/asm/errata_list_vendors.h | 3 +- arch/riscv/include/asm/rwonce.h | 34 ++++++++++++++++++++ include/asm-generic/rwonce.h | 2 ++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/include/asm/rwonce.h diff --git a/arch/riscv/Kconfig.errata b/arch/riscv/Kconfig.errata index 3c945d086c7d02..4d3f13522da6d0 100644 --- a/arch/riscv/Kconfig.errata +++ b/arch/riscv/Kconfig.errata @@ -154,4 +154,21 @@ config ERRATA_THEAD_GHOSTWRITE If you don't know what to do here, say "Y". +config ERRATA_THEAD_WRITE_ONCE + bool "Apply T-Head WRITE_ONCE errata" + depends on ERRATA_THEAD + default y + help + The early version of T-Head C9xx cores of sg2042 & th1520 have a store + merge buffer delay problem. The store merge buffer could improve the + store queue performance by merging multi-store requests, but when there + are no continued store requests, the prior single store request would be + waiting in the store queue for a long time. That would cause signifi- + cant problems for communication between multi-cores. Appending a + fence w.o could immediately flush the store merge buffer and let other + cores see the write result. + + This will apply the WRITE_ONCE errata to handle the non-standard beh- + avior via appending a fence w.o instruction for WRITE_ONCE(). + endmenu # "CPU errata selection" diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c index 6f1c683f0ec037..64683882d50b75 100644 --- a/arch/riscv/errata/thead/errata.c +++ b/arch/riscv/errata/thead/errata.c @@ -168,6 +168,23 @@ static bool errata_probe_ghostwrite(unsigned int stage, return true; } +static bool errata_probe_write_once(unsigned int stage, + unsigned long arch_id, unsigned long impid) +{ + if (!IS_ENABLED(CONFIG_ERRATA_THEAD_WRITE_ONCE)) + return false; + + /* target-c9xx cores report arch_id and impid as 0 */ + if (arch_id != 0 || impid != 0) + return false; + + if (stage == RISCV_ALTERNATIVES_BOOT || + stage == RISCV_ALTERNATIVES_MODULE) + return true; + + return false; +} + static u32 thead_errata_probe(unsigned int stage, unsigned long archid, unsigned long impid) { @@ -183,6 +200,9 @@ static u32 thead_errata_probe(unsigned int stage, errata_probe_ghostwrite(stage, archid, impid); + if (errata_probe_write_once(stage, archid, impid)) + cpu_req_errata |= BIT(ERRATA_THEAD_WRITE_ONCE); + return cpu_req_errata; } diff --git a/arch/riscv/include/asm/errata_list_vendors.h b/arch/riscv/include/asm/errata_list_vendors.h index ec7eba3734371a..8fd7c36307e2ac 100644 --- a/arch/riscv/include/asm/errata_list_vendors.h +++ b/arch/riscv/include/asm/errata_list_vendors.h @@ -18,7 +18,8 @@ #define ERRATA_THEAD_MAE 0 #define ERRATA_THEAD_PMU 1 #define ERRATA_THEAD_GHOSTWRITE 2 -#define ERRATA_THEAD_NUMBER 3 +#define ERRATA_THEAD_WRITE_ONCE 3 +#define ERRATA_THEAD_NUMBER 4 #endif #ifdef CONFIG_ERRATA_MIPS diff --git a/arch/riscv/include/asm/rwonce.h b/arch/riscv/include/asm/rwonce.h new file mode 100644 index 00000000000000..081793d4d772da --- /dev/null +++ b/arch/riscv/include/asm/rwonce.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_RWONCE_H +#define __ASM_RWONCE_H + +#include +#include +#include +#include + +#if defined(CONFIG_ERRATA_THEAD_WRITE_ONCE) && !defined(NO_ALTERNATIVE) + +#define write_once_fence() \ +do { \ + asm volatile(ALTERNATIVE( \ + "nop", \ + "fence w, o", \ + THEAD_VENDOR_ID, \ + ERRATA_THEAD_WRITE_ONCE, \ + CONFIG_ERRATA_THEAD_WRITE_ONCE) \ + : : : "memory"); \ +} while (0) + +#define __WRITE_ONCE(x, val) \ +do { \ + *(volatile typeof(x) *)&(x) = (val); \ + write_once_fence(); \ +} while (0) + +#endif /* defined(CONFIG_ERRATA_THEAD_WRITE_ONCE) && !defined(NO_ALTERNATIVE) */ + +#include + +#endif /* __ASM_RWONCE_H */ diff --git a/include/asm-generic/rwonce.h b/include/asm-generic/rwonce.h index 52b969c7cef935..4e2d941f15a11b 100644 --- a/include/asm-generic/rwonce.h +++ b/include/asm-generic/rwonce.h @@ -50,10 +50,12 @@ __READ_ONCE(x); \ }) +#ifndef __WRITE_ONCE #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) +#endif #define WRITE_ONCE(x, val) \ do { \ From 6e881e68cad2200a7d8c297bc93a071a1823e1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Thu, 18 Sep 2025 13:58:56 -0700 Subject: [PATCH 129/521] FROMLIST: PCI: Release BAR0 of an integrated bridge to allow GPU BAR resize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resizing BAR to a larger size has to release upstream bridge windows in order make the bridge windows larger as well (and to potential relocate them into a larger free block within iomem space). Some GPUs have an integrated PCI switch that has BAR0. The resource allocation assigns space for that BAR0 as it does for any resource. An extra resource on a bridge will pin its upstream bridge window in place which prevents BAR resize for anything beneath that bridge. Nothing in the pcieport driver provided by PCI core, which typically is the driver bound to these bridges, requires that BAR0. Because of that, releasing the extra BAR does not seem to have notable downsides but comes with a clear upside. Therefore, release BAR0 of such switches using a quirk and clear its flags to prevent any new invocation of the resource assignment algorithm from assigning the resource again. Due to other siblings within the PCI hierarchy of all the devices integrated into the GPU, some other devices may still have to be manually removed before the resize is free of any bridge window pins. Such siblings can be released through sysfs to unpin windows while leaving access to GPU's sysfs entries required for initiating the resize operation, whereas removing the topmost bridge this quirk targets would result in removing the GPU device as well so no manual workaround for this problem exists. Reported-by: Lucas De Marchi Link: https://lore.kernel.org/linux-pci/fl6tx5ztvttg7txmz2ps7oyd745wg3lwcp3h7esmvnyg26n44y@owo2ojiu2mov/ Link: https://lore.kernel.org/intel-xe/20250721173057.867829-1-uwu@icenowy.me/ Signed-off-by: Ilpo Järvinen Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Lucas De Marchi Link: https://lore.kernel.org/intel-xe/fafda2a3-fc63-ce97-d22b-803f771a4d19@linux.intel.com Link: https://lore.kernel.org/r/20250918-xe-pci-rebar-2-v1-1-6c094702a074@intel.com Signed-off-by: Han Gao Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index dd0025d3914e60..0c2580361b3981 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6366,3 +6366,26 @@ static void pci_mask_replay_timer_timeout(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9750, pci_mask_replay_timer_timeout); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9755, pci_mask_replay_timer_timeout); #endif + +/* + * PCI switches integrated into Intel Arc GPUs have BAR0 that prevents + * resizing the BARs of the GPU device due to that bridge BAR0 pinning the + * bridge window it's under in place. Nothing in pcieport requires that + * BAR0. + * + * Release and disable BAR0 permanently by clearing its flags to prevent + * anything from assigning it again. + */ +static void pci_release_bar0(struct pci_dev *pdev) +{ + struct resource *res = pci_resource_n(pdev, 0); + + if (!res->parent) + return; + + pci_release_resource(pdev, 0); + res->flags = 0; +} +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, 0x4fa0, pci_release_bar0); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, 0x4fa1, pci_release_bar0); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, 0xe2ff, pci_release_bar0); From 068bbe3fdbd6d7f589c5f7e81153ac9817cc275d Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Thu, 20 Nov 2025 13:14:16 +0000 Subject: [PATCH 130/521] FROMLIST: NFU: riscv: dts: thead: Add CPU clock and OPP table for TH1520 Add operating point table for CPU cores, and wire up clocks for CPU nodes. This patch isn't intended for upstreaming but only for testing purpose, since the PMIC driver for scaling CPU voltage isn't ready yet. Only operating points whose voltage is satisified by Lichee Module 4A's PMIC default, i.e. <= 1.5GHz, are enabled. Signed-off-by: Yao Zi Link: https://lore.kernel.org/r/20251120131416.26236-8-ziyao@disroot.org Signed-off-by: Han Gao --- arch/riscv/boot/dts/thead/th1520.dtsi | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index 94932c51b7e392..f2abc6338458ba 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -38,6 +38,8 @@ d-cache-sets = <512>; next-level-cache = <&l2_cache>; mmu-type = "riscv,sv39"; + operating-points-v2 = <&cpu_opp>; + clocks = <&clk CLK_C910>; cpu0_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -65,6 +67,8 @@ d-cache-sets = <512>; next-level-cache = <&l2_cache>; mmu-type = "riscv,sv39"; + operating-points-v2 = <&cpu_opp>; + clocks = <&clk CLK_C910>; cpu1_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -92,6 +96,8 @@ d-cache-sets = <512>; next-level-cache = <&l2_cache>; mmu-type = "riscv,sv39"; + operating-points-v2 = <&cpu_opp>; + clocks = <&clk CLK_C910>; cpu2_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -119,6 +125,8 @@ d-cache-sets = <512>; next-level-cache = <&l2_cache>; mmu-type = "riscv,sv39"; + operating-points-v2 = <&cpu_opp>; + clocks = <&clk CLK_C910>; cpu3_intc: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -137,6 +145,33 @@ }; }; + cpu_opp: opp-table-cpu { + compatible = "operating-points-v2"; + opp-shared; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + opp-microvolt = <600000>; + }; + + opp-800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <700000>; + }; + + opp-1500000000 { + opp-hz = /bits/ 64 <1500000000>; + opp-microvolt = <800000>; + }; + +/* + opp-1848000000 { + opp-hz = /bits/ 64 <1848000000>; + opp-microvolt = <1000000>; + }; + */ + }; + pmu { compatible = "riscv,pmu"; riscv,event-to-mhpmcounters = From 7b2be628b5646b07b40926ee190ad8b656db0309 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Thu, 27 Nov 2025 16:02:03 +0800 Subject: [PATCH 131/521] FROMLIST: Input: Add driver for PixArt PS/2 touchpad This patch introduces a driver for the PixArt PS/2 touchpad, which supports both clickpad and touchpad types. At the same time, we extended the single data packet length to 16, because according to the current PixArt hardware and FW design, we need 11 bytes/15 bytes to represent the complete three-finger/four-finger data. Co-developed-by: Jon Xie Signed-off-by: Jon Xie Co-developed-by: Jay Lee Signed-off-by: Jay Lee Signed-off-by: Binbin Zhou Tested-by: Kexy Biscuit Link: https://lore.kernel.org/loongarch/20251127080203.3218018-1-zhoubinbin@loongson.cn/ Signed-off-by: Mingcong Bai --- drivers/input/mouse/Kconfig | 12 ++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/pixart_ps2.c | 310 +++++++++++++++++++++++++++++ drivers/input/mouse/pixart_ps2.h | 36 ++++ drivers/input/mouse/psmouse-base.c | 17 ++ drivers/input/mouse/psmouse.h | 3 +- 6 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 drivers/input/mouse/pixart_ps2.c create mode 100644 drivers/input/mouse/pixart_ps2.h diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 074130d8895381..086b2e9d443e47 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -69,6 +69,18 @@ config MOUSE_PS2_LOGIPS2PP If unsure, say Y. +config MOUSE_PS2_PIXART + bool "PixArt PS/2 touchpad protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + This driver supports the PixArt PS/2 touchpad found in some + laptops. + Say Y here if you have a PixArt PS/2 TouchPad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_SYNAPTICS bool "Synaptics PS/2 mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index ed40f141691e2f..0572a02be2388a 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -28,6 +28,7 @@ psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o +psmouse-$(CONFIG_MOUSE_PS2_PIXART) += pixart_ps2.o psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o diff --git a/drivers/input/mouse/pixart_ps2.c b/drivers/input/mouse/pixart_ps2.c new file mode 100644 index 00000000000000..682aa6499e63c5 --- /dev/null +++ b/drivers/input/mouse/pixart_ps2.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Pixart Touchpad Controller 1336U PS2 driver + * + * Author: Jon Xie + * Jay Lee + * Further cleanup and restructuring by: + * Binbin Zhou + * + * Copyright (C) 2021-2024 Pixart Imaging. + * Copyright (C) 2024-2025 Loongson Technology Corporation Limited. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pixart_ps2.h" + +static int pixart_read_tp_mode(struct ps2dev *ps2dev, u8 *mode) +{ + int error; + u8 param[1] = { 0 }; + + error = ps2_command(ps2dev, param, PIXART_CMD_REPORT_FORMAT); + if (error) + return error; + + *mode = param[0] == 1 ? PIXART_MODE_ABS : PIXART_MODE_REL; + + return 0; +} + +static int pixart_read_tp_type(struct ps2dev *ps2dev, u8 *type) +{ + int error; + u8 param[3] = { 0 }; + + param[0] = 0xa; + error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + if (error) + return error; + + param[0] = 0x0; + error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + if (error) + return error; + + error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + if (error) + return error; + + error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + if (error) + return error; + + param[0] = 0x3; + error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + if (error) + return error; + + error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); + if (error) + return error; + + switch (param[0]) { + case 0xc: + *type = PIXART_TYPE_CLICKPAD; + break; + case 0xe: + *type = PIXART_TYPE_TOUCHPAD; + break; + default: + return -EIO; + } + + return 0; +} + +static void pixart_reset(struct psmouse *psmouse) +{ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + + /* according to PixArt, 100ms is required for the upcoming reset */ + msleep(100); + psmouse_reset(psmouse); +} + +static void pixart_process_packet(struct psmouse *psmouse) +{ + struct pixart_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + const u8 *pkt = psmouse->packet; + unsigned int contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]); + unsigned int i, id, abs_x, abs_y; + bool tip; + + for (i = 0; i < contact_cnt; i++) { + const u8 *p = &pkt[i * 3]; + + id = FIELD_GET(SLOT_ID_MASK, p[3]); + abs_y = FIELD_GET(ABS_Y_MASK, p[3]) << 8 | p[1]; + abs_x = FIELD_GET(ABS_X_MASK, p[3]) << 8 | p[2]; + + if (i == PIXART_MAX_FINGERS - 1) + tip = pkt[14] & BIT(1); + else + tip = pkt[3 * contact_cnt + 1] & BIT(2 * i + 1); + + input_mt_slot(dev, id); + if (input_mt_report_slot_state(dev, MT_TOOL_FINGER, tip)) { + input_report_abs(dev, ABS_MT_POSITION_Y, abs_y); + input_report_abs(dev, ABS_MT_POSITION_X, abs_x); + } + } + + input_mt_sync_frame(dev); + + if (priv->type == PIXART_TYPE_CLICKPAD) { + input_report_key(dev, BTN_LEFT, pkt[0] & 0x03); + } else { + input_report_key(dev, BTN_LEFT, pkt[0] & BIT(0)); + input_report_key(dev, BTN_RIGHT, pkt[0] & BIT(1)); + } + + input_sync(dev); +} + +static psmouse_ret_t pixart_protocol_handler(struct psmouse *psmouse) +{ + u8 *pkt = psmouse->packet; + u8 contact_cnt; + + if ((pkt[0] & 0x8c) != 0x80) + return PSMOUSE_BAD_DATA; + + contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]); + if (contact_cnt > PIXART_MAX_FINGERS) + return PSMOUSE_BAD_DATA; + + if (contact_cnt == PIXART_MAX_FINGERS && + psmouse->pktcnt < psmouse->pktsize) { + return PSMOUSE_GOOD_DATA; + } + + if (contact_cnt == 0 && psmouse->pktcnt < 5) + return PSMOUSE_GOOD_DATA; + + if (psmouse->pktcnt < 3 * contact_cnt + 2) + return PSMOUSE_GOOD_DATA; + + pixart_process_packet(psmouse); + + return PSMOUSE_FULL_PACKET; +} + +static void pixart_disconnect(struct psmouse *psmouse) +{ + pixart_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; +} + +static int pixart_reconnect(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + u8 mode; + int error; + + pixart_reset(psmouse); + + error = pixart_read_tp_mode(ps2dev, &mode); + if (error) + return error; + + if (mode != PIXART_MODE_ABS) + return -EIO; + + error = ps2_command(ps2dev, NULL, PIXART_CMD_SWITCH_PROTO); + if (error) + return error; + + return 0; +} + +static int pixart_set_input_params(struct input_dev *dev, + struct pixart_data *priv) +{ + /* No relative support */ + __clear_bit(EV_REL, dev->evbit); + __clear_bit(REL_X, dev->relbit); + __clear_bit(REL_Y, dev->relbit); + __clear_bit(BTN_MIDDLE, dev->keybit); + + /* Buttons */ + __set_bit(EV_KEY, dev->evbit); + __set_bit(BTN_LEFT, dev->keybit); + if (priv->type == PIXART_TYPE_CLICKPAD) + __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); + else + __set_bit(BTN_RIGHT, dev->keybit); + + /* Absolute position */ + input_set_abs_params(dev, ABS_X, 0, PIXART_PAD_WIDTH, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, PIXART_PAD_HEIGHT, 0, 0); + + input_set_abs_params(dev, ABS_MT_POSITION_X, + 0, PIXART_PAD_WIDTH, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, + 0, PIXART_PAD_HEIGHT, 0, 0); + + return input_mt_init_slots(dev, PIXART_MAX_FINGERS, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); +} + +static int pixart_query_hardware(struct ps2dev *ps2dev, u8 *mode, u8 *type) +{ + int error; + + error = pixart_read_tp_type(ps2dev, type); + if (error) + return error; + + error = pixart_read_tp_mode(ps2dev, mode); + if (error) + return error; + + return 0; +} + +int pixart_detect(struct psmouse *psmouse, bool set_properties) +{ + u8 type; + int error; + + pixart_reset(psmouse); + + error = pixart_read_tp_type(&psmouse->ps2dev, &type); + if (error) + return error; + + if (set_properties) { + psmouse->vendor = "PixArt"; + psmouse->name = (type == PIXART_TYPE_TOUCHPAD) ? + "touchpad" : "clickpad"; + } + + return 0; +} + +int pixart_init(struct psmouse *psmouse) +{ + int error; + struct pixart_data *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + psmouse->private = priv; + pixart_reset(psmouse); + + error = pixart_query_hardware(&psmouse->ps2dev, + &priv->mode, &priv->type); + if (error) { + psmouse_err(psmouse, "init: Unable to query PixArt touchpad hardware.\n"); + goto err_exit; + } + + /* Relative mode follows standard PS/2 mouse protocol */ + if (priv->mode != PIXART_MODE_ABS) { + error = -EIO; + goto err_exit; + } + + /* Set absolute mode */ + error = ps2_command(&psmouse->ps2dev, NULL, PIXART_CMD_SWITCH_PROTO); + if (error) { + psmouse_err(psmouse, "init: Unable to initialize PixArt absolute mode.\n"); + goto err_exit; + } + + error = pixart_set_input_params(psmouse->dev, priv); + if (error) { + psmouse_err(psmouse, "init: Unable to set input params.\n"); + goto err_exit; + } + + psmouse->pktsize = 15; + psmouse->protocol_handler = pixart_protocol_handler; + psmouse->disconnect = pixart_disconnect; + psmouse->reconnect = pixart_reconnect; + psmouse->cleanup = pixart_reset; + /* resync is not supported yet */ + psmouse->resync_time = 0; + + return 0; + +err_exit: + pixart_reset(psmouse); + kfree(priv); + psmouse->private = NULL; + return error; +} diff --git a/drivers/input/mouse/pixart_ps2.h b/drivers/input/mouse/pixart_ps2.h new file mode 100644 index 00000000000000..47a1d040f2d10f --- /dev/null +++ b/drivers/input/mouse/pixart_ps2.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _PIXART_PS2_H +#define _PIXART_PS2_H + +#include "psmouse.h" + +#define PIXART_PAD_WIDTH 1023 +#define PIXART_PAD_HEIGHT 579 +#define PIXART_MAX_FINGERS 4 + +#define PIXART_CMD_REPORT_FORMAT 0x01d8 +#define PIXART_CMD_SWITCH_PROTO 0x00de + +#define PIXART_MODE_REL 0 +#define PIXART_MODE_ABS 1 + +#define PIXART_TYPE_CLICKPAD 0 +#define PIXART_TYPE_TOUCHPAD 1 + +#define CONTACT_CNT_MASK GENMASK(6, 4) + +#define SLOT_ID_MASK GENMASK(2, 0) +#define ABS_Y_MASK GENMASK(5, 4) +#define ABS_X_MASK GENMASK(7, 6) + +struct pixart_data { + u8 mode; + u8 type; + int x_max; + int y_max; +}; + +int pixart_detect(struct psmouse *psmouse, bool set_properties); +int pixart_init(struct psmouse *psmouse); + +#endif /* _PIXART_PS2_H */ diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 6ab5f1d96eaeaa..6a3faaedd4f764 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -35,6 +35,7 @@ #include "focaltech.h" #include "vmmouse.h" #include "byd.h" +#include "pixart_ps2.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -886,6 +887,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = byd_detect, .init = byd_init, }, +#endif +#ifdef CONFIG_MOUSE_PS2_PIXART + { + .type = PSMOUSE_PIXART, + .name = "PixArtPS/2", + .alias = "pixart", + .detect = pixart_detect, + .init = pixart_init, + }, #endif { .type = PSMOUSE_AUTO, @@ -1146,6 +1156,13 @@ static int psmouse_extensions(struct psmouse *psmouse, return ret; } + /* Try PixArt touchpad */ + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_PIXART, &max_proto, + set_properties, true)) { + return PSMOUSE_PIXART; + } + if (max_proto > PSMOUSE_IMEX) { if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS, &max_proto, set_properties, true)) diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 90ed8cd15d857b..03aaa419d074cf 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -69,6 +69,7 @@ enum psmouse_type { PSMOUSE_BYD, PSMOUSE_SYNAPTICS_SMBUS, PSMOUSE_ELANTECH_SMBUS, + PSMOUSE_PIXART, PSMOUSE_AUTO /* This one should always be last */ }; @@ -94,7 +95,7 @@ struct psmouse { const char *vendor; const char *name; const struct psmouse_protocol *protocol; - unsigned char packet[8]; + unsigned char packet[16]; unsigned char badbyte; unsigned char pktcnt; unsigned char pktsize; From 3c4e30c27a9f5b44137b06e28108833104b7b218 Mon Sep 17 00:00:00 2001 From: Asuna Yang Date: Thu, 4 Dec 2025 08:54:51 +0100 Subject: [PATCH 132/521] BACKPORT: FROMLIST: rust: export BINDGEN_TARGET from a separate Makefile A subsequent commit will add a new function `bindgen-option` to `scripts/Kconfig.include`. The bindgen backend requires the `--target` option for cross compiling, but variable `BINDGEN_TARGET` in `rust/Makefile` cannot be exported to `scripts/Kconfig.include`. Therefore, move this variable to a separate new `Makefile.rust` file and include it from `scripts/Makefile` to make the exported variable available for use in Kconfig. Place the include in the `need-compiler` branch to avoid including it in irrelevant make targets. Since the new file name is `Makefile.rust`, it matches an existing MAINTAINERS rule `scripts/*rust*`, so no modification to the MAINTAINERS file is needed. Signed-off-by: Asuna Yang Link: https://lore.kernel.org/r/20251204-gcc-rust-v5-v5-1-2d4f20d86c24@gmail.com Signed-off-by: Han Gao Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in rust/Makefile ] Signed-off-by: Mingcong Bai --- Makefile | 3 ++- rust/Makefile | 10 ---------- scripts/Makefile.rust | 11 +++++++++++ 3 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 scripts/Makefile.rust diff --git a/Makefile b/Makefile index b3496dd21312c7..8b6fb4e6878013 100644 --- a/Makefile +++ b/Makefile @@ -734,9 +734,10 @@ ifneq ($(findstring clang,$(CC_VERSION_TEXT)),) include $(srctree)/scripts/Makefile.clang endif +ifdef need-compiler +include $(srctree)/scripts/Makefile.rust # Include this also for config targets because some architectures need # cc-cross-prefix to determine CROSS_COMPILE. -ifdef need-compiler include $(srctree)/scripts/Makefile.compiler endif diff --git a/rust/Makefile b/rust/Makefile index b9e9f512cec314..da5b2fb61c1838 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -398,16 +398,6 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ -fdiagnostics-show-context -fdiagnostics-show-context=% \ --param=% --param asan-% -fno-isolate-erroneous-paths-dereference -# Derived from `scripts/Makefile.clang`. -BINDGEN_TARGET_x86 := x86_64-linux-gnu -BINDGEN_TARGET_arm64 := aarch64-linux-gnu -BINDGEN_TARGET_arm := arm-linux-gnueabi -BINDGEN_TARGET_loongarch := loongarch64-linux-gnusf -# This is only for i386 UM builds, which need the 32-bit target not -m32 -BINDGEN_TARGET_i386 := i386-linux-gnu -BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) -BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) - # All warnings are inhibited since GCC builds are very experimental, # many GCC warnings are not supported by Clang, they may only appear in # some configurations, with new GCC versions, etc. diff --git a/scripts/Makefile.rust b/scripts/Makefile.rust new file mode 100644 index 00000000000000..fd48b0e0d46fbb --- /dev/null +++ b/scripts/Makefile.rust @@ -0,0 +1,11 @@ +# Derived from `scripts/Makefile.clang`. +BINDGEN_TARGET_x86 := x86_64-linux-gnu +BINDGEN_TARGET_arm64 := aarch64-linux-gnu +BINDGEN_TARGET_arm := arm-linux-gnueabi +BINDGEN_TARGET_loongarch := loongarch64-linux-gnusf +# This is only for i386 UM builds, which need the 32-bit target not -m32 +BINDGEN_TARGET_i386 := i386-linux-gnu +BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) +BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) + +export BINDGEN_TARGET From 87f7c2aa02c70a4f35d857376144a6aef38b3ece Mon Sep 17 00:00:00 2001 From: Asuna Yang Date: Thu, 4 Dec 2025 08:54:52 +0100 Subject: [PATCH 133/521] FROMLIST: rust: generate a fatal error if BINDGEN_TARGET is undefined Generate a friendly fatal error if the target triplet is undefined for bindgen, rather than having the compiler generate obscure error messages during the build stage. This piece of code is copied from `scripts/Makefile.clang`. Before this commit, error messages might look like: error: unknown argument: '-mno-riscv-attribute' error: unsupported argument 'medany' to option '-mcmodel=' for target 'unknown' error: unsupported option '-march=' for target '' error: unsupported option '-mno-save-restore' for target '' error: unknown target triple 'unknown' panicked at bindgen/ir/context.rs:562:15: libclang error; possible causes include: - Invalid flag syntax - Unrecognized flags - Invalid flag arguments - File I/O errors - Host vs. target architecture mismatch Acked-by: Miguel Ojeda Signed-off-by: Asuna Yang Link: https://lore.kernel.org/r/20251204-gcc-rust-v5-v5-2-2d4f20d86c24@gmail.com Signed-off-by: Han Gao Signed-off-by: Mingcong Bai --- scripts/Makefile.rust | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/Makefile.rust b/scripts/Makefile.rust index fd48b0e0d46fbb..c3b8c2435bae9a 100644 --- a/scripts/Makefile.rust +++ b/scripts/Makefile.rust @@ -8,4 +8,8 @@ BINDGEN_TARGET_i386 := i386-linux-gnu BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) +ifeq ($(BINDGEN_TARGET),) +$(error add '--target=' option to scripts/Makefile.rust) +else export BINDGEN_TARGET +endif From fc544b29bac951bb6cdfbe9ef9c0109218ed5b58 Mon Sep 17 00:00:00 2001 From: Asuna Yang Date: Thu, 4 Dec 2025 08:54:53 +0100 Subject: [PATCH 134/521] FROMLIST: rust: add a Kconfig function to test for support of bindgen options Add a new `bindgen-backend-option` Kconfig function to test whether the bindgen backend supports a given flag. A subsequent commit will use this function to test for RISC-V extension flags. Signed-off-by: Asuna Yang Link: https://lore.kernel.org/r/20251204-gcc-rust-v5-v5-3-2d4f20d86c24@gmail.com Signed-off-by: Han Gao Signed-off-by: Mingcong Bai --- scripts/Kconfig.include | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include index fc10671c297cf3..d381cf4b40e608 100644 --- a/scripts/Kconfig.include +++ b/scripts/Kconfig.include @@ -76,3 +76,8 @@ rustc-llvm-version := $(shell,$(srctree)/scripts/rustc-llvm-version.sh $(RUSTC)) # If you are testing for unstable features, consider testing RUSTC_VERSION # instead, as features may have different completeness while available. rustc-option = $(success,trap "rm -rf .tmp_$$" EXIT; mkdir .tmp_$$; $(RUSTC) $(1) --crate-type=rlib /dev/null --out-dir=.tmp_$$ -o .tmp_$$/tmp.rlib) + +# $(bindgen-backend-option,) +# Return y if bindgen backend supports , n otherwise +# For now, the backend refers only to libclang, so more specifically, this function tests whether the given flag is recognized by the libclang used by bindgen. +bindgen-backend-option = $(success,trap "rm -f .tmp_$$.h" EXIT; touch .tmp_$$.h; $(BINDGEN) .tmp_$$.h -- --target=$(BINDGEN_TARGET) $(1)) From d36a26a13943de92fe9760ba74ad659d031cdcaf Mon Sep 17 00:00:00 2001 From: Asuna Yang Date: Thu, 4 Dec 2025 08:54:54 +0100 Subject: [PATCH 135/521] BACKPORT: FROMLIST: RISC-V: handle extension configs for bindgen, re-enable gcc + rust builds Commit 33549fcf37ec ("RISC-V: disallow gcc + rust builds") disabled GCC + Rust builds for RISC-V due to differences in extension handling compared to LLVM. This commit enables GCC + Rust builds again. Add `bindgen-option` conditions for the availability of libclang to the RISC-V extension Kconfig symbols that depend on the `cc-option` function. For Zicsr/Zifencei special handling, since LLVM/Clang always enables these two extensions, either don't pass them to `-march`, or pass them explicitly and Rust bindgen libclang must recognize them. Clang does not support `-mno-riscv-attribute` flag, filter it out to resolve error: unknown argument: '-mno-riscv-attribute'. Define `BINDGEN_TARGET_riscv` to pass the target triplet to Rust bindgen libclang for RISC-V to resolve error: unsupported argument 'medany' to option '-mcmodel=' for target 'unknown'. Update the documentation, GCC + Rust builds for RISC-V are now maintained. Acked-by: Miguel Ojeda Signed-off-by: Asuna Yang Link: https://lore.kernel.org/r/20251204-gcc-rust-v5-v5-4-2d4f20d86c24@gmail.com Signed-off-by: Han Gao [Mingcong Bai: (6.19-rc8) Resolved a minor merge conflict in rust/Makefile] Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in scripts/Makefile.rust ] Signed-off-by: Mingcong Bai --- Documentation/rust/arch-support.rst | 2 +- arch/riscv/Kconfig | 35 ++++++++++++++++++++++++++++- rust/Makefile | 3 ++- scripts/Makefile.rust | 1 + 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Documentation/rust/arch-support.rst b/Documentation/rust/arch-support.rst index 6e6a515d08991a..5282e0e174e8de 100644 --- a/Documentation/rust/arch-support.rst +++ b/Documentation/rust/arch-support.rst @@ -18,7 +18,7 @@ Architecture Level of support Constraints ``arm`` Maintained ARMv7 Little Endian only. ``arm64`` Maintained Little Endian only. ``loongarch`` Maintained \- -``riscv`` Maintained ``riscv64`` and LLVM/Clang only. +``riscv`` Maintained ``riscv64`` only. ``um`` Maintained \- ``x86`` Maintained ``x86_64`` only. ============= ================ ============================================== diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 390ae76c31ec29..a6d24c1d51ef30 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -197,7 +197,7 @@ config RISCV select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RETHOOK select HAVE_RSEQ - select HAVE_RUST if RUSTC_SUPPORTS_RISCV && CC_IS_CLANG + select HAVE_RUST if RUSTC_SUPPORTS_RISCV && TOOLCHAIN_MATCHES_ZICSR_ZIFENCEI select HAVE_SAMPLE_FTRACE_DIRECT select HAVE_SAMPLE_FTRACE_DIRECT_MULTI select HAVE_STACKPROTECTOR @@ -617,6 +617,8 @@ config TOOLCHAIN_HAS_V depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32imv) depends on LD_IS_LLD || LD_VERSION >= 23800 depends on AS_HAS_OPTION_ARCH + depends on !RUST || !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64imv) + depends on !RUST || !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32imv) config RISCV_ISA_V bool "Vector extension support" @@ -681,6 +683,8 @@ config TOOLCHAIN_HAS_ZABHA depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zabha) depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zabha) depends on AS_HAS_OPTION_ARCH + depends on !RUST || !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64ima_zabha) + depends on !RUST || !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32ima_zabha) config RISCV_ISA_ZABHA bool "Zabha extension support for atomic byte/halfword operations" @@ -699,6 +703,8 @@ config TOOLCHAIN_HAS_ZACAS depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zacas) depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zacas) depends on AS_HAS_OPTION_ARCH + depends on !RUST || !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64ima_zacas) + depends on !RUST || !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32ima_zacas) config RISCV_ISA_ZACAS bool "Zacas extension support for atomic CAS" @@ -717,6 +723,8 @@ config TOOLCHAIN_HAS_ZBB depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zbb) depends on LD_IS_LLD || LD_VERSION >= 23900 depends on AS_HAS_OPTION_ARCH + depends on !RUST || !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64ima_zbb) + depends on !RUST || !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32ima_zbb) # This symbol indicates that the toolchain supports all v1.0 vector crypto # extensions, including Zvk*, Zvbb, and Zvbc. LLVM added all of these at once. @@ -732,6 +740,8 @@ config TOOLCHAIN_HAS_ZBA depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zba) depends on LD_IS_LLD || LD_VERSION >= 23900 depends on AS_HAS_OPTION_ARCH + depends on !RUST || !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64ima_zba) + depends on !RUST || !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32ima_zba) config RISCV_ISA_ZBA bool "Zba extension support for bit manipulation instructions" @@ -767,6 +777,8 @@ config TOOLCHAIN_HAS_ZBC depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zbc) depends on LD_IS_LLD || LD_VERSION >= 23900 depends on AS_HAS_OPTION_ARCH + depends on !RUST || !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64ima_zbc) + depends on !RUST || !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32ima_zbc) config RISCV_ISA_ZBC bool "Zbc extension support for carry-less multiplication instructions" @@ -790,6 +802,8 @@ config TOOLCHAIN_HAS_ZBKB depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zbkb) depends on LD_IS_LLD || LD_VERSION >= 23900 depends on AS_HAS_OPTION_ARCH + depends on !RUST || !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64ima_zbkb) + depends on !RUST || !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32ima_zbkb) config RISCV_ISA_ZBKB bool "Zbkb extension support for bit manipulation instructions" @@ -891,6 +905,25 @@ config TOOLCHAIN_NEEDS_OLD_ISA_SPEC versions of clang and GCC to be passed to GAS, which has the same result as passing zicsr and zifencei to -march. +config RUST_BINDGEN_HAS_ZICSR_ZIFENCEI + def_bool y + depends on !64BIT || $(bindgen-backend-option,-mabi=lp64 -march=rv64ima_zicsr_zifencei) + depends on !32BIT || $(bindgen-backend-option,-mabi=ilp32 -march=rv32ima_zicsr_zifencei) + +config TOOLCHAIN_MATCHES_ZICSR_ZIFENCEI + def_bool y + # https://github.com/llvm/llvm-project/commit/22e199e6afb1263c943c0c0d4498694e15bf8a16 + depends on TOOLCHAIN_NEEDS_OLD_ISA_SPEC || !TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI || RUST_BINDGEN_HAS_ZICSR_ZIFENCEI + help + LLVM/Clang >= 17.0.0 starts recognizing Zicsr/Zifencei in -march, passing + them to -march doesn't generate an error anymore, and passing them or not + doesn't have any real difference, it still follows ISA before version + 20190608 - Zicsr/Zifencei are included in base ISA. + + The current latest version of LLVM/Clang still does not require explicit + Zicsr/Zifencei to enable these two extensions, Clang just accepts them in + -march and then silently ignores them. + config FPU bool "FPU support" default y diff --git a/rust/Makefile b/rust/Makefile index da5b2fb61c1838..5f3946d1792129 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -396,7 +396,8 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ -fstrict-flex-arrays=% -fmin-function-alignment=% \ -fzero-init-padding-bits=% -mno-fdpic \ -fdiagnostics-show-context -fdiagnostics-show-context=% \ - --param=% --param asan-% -fno-isolate-erroneous-paths-dereference + --param=% --param asan-% -fno-isolate-erroneous-paths-dereference \ + -mno-riscv-attribute # All warnings are inhibited since GCC builds are very experimental, # many GCC warnings are not supported by Clang, they may only appear in diff --git a/scripts/Makefile.rust b/scripts/Makefile.rust index c3b8c2435bae9a..1f590268624426 100644 --- a/scripts/Makefile.rust +++ b/scripts/Makefile.rust @@ -6,6 +6,7 @@ BINDGEN_TARGET_loongarch := loongarch64-linux-gnusf # This is only for i386 UM builds, which need the 32-bit target not -m32 BINDGEN_TARGET_i386 := i386-linux-gnu BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) +BINDGEN_TARGET_riscv := riscv64-linux-gnu BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) ifeq ($(BINDGEN_TARGET),) From c145ef25d848aac90975bdd61437c1f0a152c04f Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Wed, 14 Aug 2024 20:09:15 +0800 Subject: [PATCH 136/521] BACKPORT: FROMLIST: ACPI: PCI: check if the root io space is page aligned When the IO resource given by _CRS method is not page aligned, especially when the page size is larger than 4KB, serious problems will happen because the misaligned address is passed down to pci_remap_iospace(), then to vmap_page_range(), and finally to vmap_pte_range(), where the length between addr and end is expected to be divisible by PAGE_SIZE, or the loop will overrun till the pfn_none check fails. Signed-off-by: Miao Wang Link: https://lore.kernel.org/linux-acpi/20240814-check_pci_probe_res-v3-1-b6eaa6f99032@gmail.com/ [Mingcong Bai: Fixed a minor merge conflict in drivers/acpi/pci_root.c.] Signed-off-by: Mingcong Bai --- drivers/acpi/pci_root.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index a0ba64e45e8ab3..de9d14fa9528ad 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -853,7 +853,7 @@ static void acpi_pci_root_validate_resources(struct device *dev, } } -static void acpi_pci_root_remap_iospace(const struct fwnode_handle *fwnode, +static void acpi_pci_root_remap_iospace(struct acpi_device *device, struct resource_entry *entry) { #ifdef PCI_IOBASE @@ -863,7 +863,15 @@ static void acpi_pci_root_remap_iospace(const struct fwnode_handle *fwnode, resource_size_t length = resource_size(res); unsigned long port; - if (pci_register_io_range(fwnode, cpu_addr, length)) + if (!PAGE_ALIGNED(cpu_addr) || !PAGE_ALIGNED(length) || + !PAGE_ALIGNED(pci_addr)) { + dev_err(&device->dev, + FW_BUG "I/O resource %pR or its offset %pa is not page aligned\n", + res, &entry->offset); + goto err; + } + + if (pci_register_io_range(&device->fwnode, cpu_addr, length)) goto err; port = pci_address_to_pio(cpu_addr); @@ -905,7 +913,7 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) else { resource_list_for_each_entry_safe(entry, tmp, list) { if (entry->res->flags & IORESOURCE_IO) - acpi_pci_root_remap_iospace(&device->fwnode, + acpi_pci_root_remap_iospace(device, entry); if (entry->res->flags & IORESOURCE_DISABLED) From adbbd65d08f4f2a8e82ac859c164724352b881d8 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 15 Jan 2026 12:03:45 +0800 Subject: [PATCH 137/521] FROMLIST: MIPS: Loongson64: dts: sort nodes The RTC's address is after UARTs, however the node is currently before them. Re-order the node to match address sequence. Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/mips/boot/dts/loongson/ls7a-pch.dtsi | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi index 6dee85909f5a61..59ca1ef0a7b64e 100644 --- a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi +++ b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi @@ -19,13 +19,6 @@ #interrupt-cells = <2>; }; - rtc0: rtc@100d0100 { - compatible = "loongson,ls7a-rtc"; - reg = <0 0x100d0100 0 0x78>; - interrupt-parent = <&pic>; - interrupts = <52 IRQ_TYPE_LEVEL_HIGH>; - }; - ls7a_uart0: serial@10080000 { compatible = "ns16550a"; reg = <0 0x10080000 0 0x100>; @@ -65,6 +58,13 @@ no-loopback-test; }; + rtc0: rtc@100d0100 { + compatible = "loongson,ls7a-rtc"; + reg = <0 0x100d0100 0 0x78>; + interrupt-parent = <&pic>; + interrupts = <52 IRQ_TYPE_LEVEL_HIGH>; + }; + pci@1a000000 { compatible = "loongson,ls7a-pci"; device_type = "pci"; From ddf0b692535eb08fa2c79fd30351ce286adfb717 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 16 Jan 2026 01:48:34 +0800 Subject: [PATCH 138/521] FROMLIST: MIPS: Loongson64: dts: add node for LS7A PCH LPC Loongson 7A series PCH contain a LPC IRQ controller. Add the device tree node of it. Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/mips/boot/dts/loongson/ls7a-pch.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi index 59ca1ef0a7b64e..d41ca0b3d6511b 100644 --- a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi +++ b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi @@ -19,6 +19,15 @@ #interrupt-cells = <2>; }; + lpc: interrupt-controller@10002000 { + compatible = "loongson,pch-lpc-1.0"; + reg = <0 0x10002000 0 0x1000>; + interrupt-controller; + interrupt-parent = <&pic>; + interrupts = <19 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <2>; + }; + ls7a_uart0: serial@10080000 { compatible = "ns16550a"; reg = <0 0x10080000 0 0x100>; From fceb1264e541188b756bdf5a8a0ff39ed7c4ca4e Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 1 Feb 2026 20:39:30 +0800 Subject: [PATCH 139/521] FROMLIST: kbuild: install-extmod-build: do not exclude scripts/dtc/libfdt/ There exists a header file in include/linux/ called libfdt.h that is just a wrapper for libfdt header file in scripts/dtc/libfdt/. This makes the headers inside libfdt copy at scripts/dtc/libfdt/ part of the kernel headers for building external modules. Do not exclude them, otherwise modules that include will fail to build externally. Fixes: aaed5c7739be ("kbuild: slim down package for building external modules") Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- scripts/package/install-extmod-build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build index f12e1ffe409eb0..4430379b991106 100755 --- a/scripts/package/install-extmod-build +++ b/scripts/package/install-extmod-build @@ -11,7 +11,8 @@ is_enabled() { find_in_scripts() { find scripts \ - \( -name atomic -o -name dtc -o -name kconfig -o -name package \) -prune -o \ + \( -name atomic -o -name kconfig -o -name package -o \ + \( -path '*/dtc/*' -a ! -path '*/libfdt*' \) \) -prune -o \ ! -name unifdef -a ! -name mk_elfconfig -a \( -type f -o -type l \) -print } From bec5f1b6575046016381e12302abf22ccb57519e Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Wed, 4 Feb 2026 09:54:17 +0530 Subject: [PATCH 140/521] BACKPORT: FROMLIST: powerpc: Enable Rust for ppc64le Enabling rust support for ppc64 only. To support rust a new macro ARCH_STATIC_BRANCH_ASM in powerpc is added to avoid duplication of inline asm between C and Rust. This is inline with commit aecaf181651c '("jump_label: adjust inline asm to be consistent")' Tested on powernv9: $ uname -rm 6.19.0-rc8+ ppc64le $ sudo modprobe rust_minimal [ 632.890850] rust_minimal: Rust minimal sample (init) [ 632.890881] rust_minimal: Am I built-in? false [ 632.890898] rust_minimal: test_parameter: 1 $ sudo rmmod rust_minimal [ 648.272832] rust_minimal: My numbers are [72, 108, 200] [ 648.272873] rust_minimal: Rust minimal sample (exit) $ sudo modprobe rust_print [ 843.410391] rust_print: Rust printing macros sample (init) [ 843.410424] rust_print: Emergency message (level 0) without args [ 843.410451] rust_print: Alert message (level 1) without args [ 843.410477] rust_print: Critical message (level 2) without args [ 843.410503] rust_print: Error message (level 3) without args [ 843.410530] rust_print: Warning message (level 4) without args [ 843.410557] rust_print: Notice message (level 5) without args [ 843.410594] rust_print: Info message (level 6) without args [ 843.410617] rust_print: A line that is continued without args [ 843.410646] rust_print: Emergency message (level 0) with args [ 843.410675] rust_print: Alert message (level 1) with args [ 843.410691] rust_print: Critical message (level 2) with args [ 843.410727] rust_print: Error message (level 3) with args [ 843.410761] rust_print: Warning message (level 4) with args [ 843.410796] rust_print: Notice message (level 5) with args [ 843.410821] rust_print: Info message (level 6) with args [ 843.410854] rust_print: A line that is continued with args [ 843.410892] rust_print: 1 [ 843.410895] rust_print: "hello, world" [ 843.410924] rust_print: [samples/rust/rust_print_main.rs:35:5] c = "hello, world" [ 843.410977] rust_print: Arc says 42 [ 843.410979] rust_print: Arc says hello, world $ sudo rmmod rust_print [ 843.411003] rust_print: "hello, world" [ 888.499935] rust_print: Rust printing macros sample (exit) Signed-off-by: Mukesh Kumar Chaurasiya (IBM) [Mingcong Bai: Follow "FROMLIST: rust: export BINDGEN_TARGET from a separate Makefile" and move rust/Makefile changes to scripts/Makefile.rust] Link: https://lore.kernel.org/all/20260204042417.83903-1-mkchauras@gmail.com/ Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in scripts/Makefile.rust ] Signed-off-by: Mingcong Bai --- arch/powerpc/Kconfig | 1 + arch/powerpc/Makefile | 9 +++++---- arch/powerpc/include/asm/jump_label.h | 23 +++++++++++++---------- scripts/Makefile.rust | 1 + scripts/generate_rust_target.rs | 24 ++++++++++++++++++++++++ 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index e93df95b79e750..592aac33cdca36 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -287,6 +287,7 @@ config PPC select HAVE_RSEQ select HAVE_SAMPLE_FTRACE_DIRECT if HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_SAMPLE_FTRACE_DIRECT_MULTI if HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + select HAVE_RUST if PPC64 select HAVE_SETUP_PER_CPU_AREA if PPC64 select HAVE_SOFTIRQ_ON_OWN_STACK select HAVE_STACKPROTECTOR if PPC32 && $(cc-option,$(m32-flag) -mstack-protector-guard=tls -mstack-protector-guard-reg=r2 -mstack-protector-guard-offset=0) diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index a58b1029592ce2..7ad28cf0fd3558 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -172,10 +172,11 @@ CFLAGS-y += $(CONFIG_TUNE_CPU) asinstr := $(call as-instr,lis 9$(comma)foo@high,-DHAVE_AS_ATHIGH=1) -KBUILD_CPPFLAGS += -I $(srctree)/arch/powerpc $(asinstr) -KBUILD_AFLAGS += $(AFLAGS-y) -KBUILD_CFLAGS += $(CC_FLAGS_NO_FPU) -KBUILD_CFLAGS += $(CFLAGS-y) +KBUILD_CPPFLAGS += -I $(srctree)/arch/powerpc $(asinstr) +KBUILD_AFLAGS += $(AFLAGS-y) +KBUILD_CFLAGS += $(CC_FLAGS_NO_FPU) +KBUILD_CFLAGS += $(CFLAGS-y) +KBUILD_RUSTFLAGS += -Ctarget-feature=-mma,-vsx,-hard-float,-altivec CHECKFLAGS += -m$(BITS) -D__powerpc__ -D__powerpc$(BITS)__ ifdef CONFIG_CPU_BIG_ENDIAN diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h index d4eaba459a0ed5..a6b211502bfe79 100644 --- a/arch/powerpc/include/asm/jump_label.h +++ b/arch/powerpc/include/asm/jump_label.h @@ -15,14 +15,20 @@ #define JUMP_ENTRY_TYPE stringify_in_c(FTR_ENTRY_LONG) #define JUMP_LABEL_NOP_SIZE 4 +#define JUMP_TABLE_ENTRY(key, label) \ + ".pushsection __jump_table, \"aw\" \n\t" \ + ".long 1b - ., " label " - . \n\t" \ + JUMP_ENTRY_TYPE key " - . \n\t" \ + ".popsection \n\t" + +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + "1: nop \n\t" \ + JUMP_TABLE_ENTRY(key,label) + static __always_inline bool arch_static_branch(struct static_key *key, bool branch) { - asm goto("1:\n\t" - "nop # arch_static_branch\n\t" - ".pushsection __jump_table, \"aw\"\n\t" - ".long 1b - ., %l[l_yes] - .\n\t" - JUMP_ENTRY_TYPE "%c0 - .\n\t" - ".popsection \n\t" + asm goto( + ARCH_STATIC_BRANCH_ASM("%c0", "%l[l_yes]") : : "i" (&((char *)key)[branch]) : : l_yes); return false; @@ -34,10 +40,7 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool { asm goto("1:\n\t" "b %l[l_yes] # arch_static_branch_jump\n\t" - ".pushsection __jump_table, \"aw\"\n\t" - ".long 1b - ., %l[l_yes] - .\n\t" - JUMP_ENTRY_TYPE "%c0 - .\n\t" - ".popsection \n\t" + JUMP_TABLE_ENTRY("%c0", "%l[l_yes]") : : "i" (&((char *)key)[branch]) : : l_yes); return false; diff --git a/scripts/Makefile.rust b/scripts/Makefile.rust index 1f590268624426..b7a6b1e2a88a5e 100644 --- a/scripts/Makefile.rust +++ b/scripts/Makefile.rust @@ -3,6 +3,7 @@ BINDGEN_TARGET_x86 := x86_64-linux-gnu BINDGEN_TARGET_arm64 := aarch64-linux-gnu BINDGEN_TARGET_arm := arm-linux-gnueabi BINDGEN_TARGET_loongarch := loongarch64-linux-gnusf +BINDGEN_TARGET_powerpc := powerpc64le-linux-gnu # This is only for i386 UM builds, which need the 32-bit target not -m32 BINDGEN_TARGET_i386 := i386-linux-gnu BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index 16f7e855e012a4..d9bc275e44ed84 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -260,6 +260,30 @@ fn main() { } } else if cfg.has("LOONGARCH") { panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target"); + } else if cfg.has("PPC64") { + ts.push("arch", "powerpc64"); + ts.push( + "data-layout", + if cfg.has("CPU_LITTLE_ENDIAN") { + "e-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512" + } else { + "E-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512" + }, + ); + ts.push("features", "-mma,-altivec,-vsx,-hard-float"); + ts.push( + "llvm-target", + if cfg.has("CPU_LITTLE_ENDIAN") { + "powerpc64le-linux-gnu" + } else { + "powerpc64-linux-gnu" + }, + ); + if cfg.rustc_version_atleast(1, 91, 0) { + ts.push("target-pointer-width", 64); + } else { + ts.push("target-pointer-width", "64"); + } } else { panic!("Unsupported architecture"); } From e16c0acdd46a4aa32e91ae6431dfd2e75f7130de Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Sat, 28 Feb 2026 17:37:04 +0000 Subject: [PATCH 141/521] FROMLIST: x86/cpu/centaur: Disable X86_FEATURE_FSGSBASE on Zhaoxin C4600 Zhaoxin C4600, which names itself as CentaurHauls, claims X86_FEATURE_FSGSBASE support in CPUID, while execution of fsgsbase- related instructions fails with #UD exception. This will cause kernel to crash early in current_save_fsgs(). Let's disable the feature on this problematic CPU and warn the user about the quirk. x86_model_id is used to match the platform to avoid unexpectedly breaking other CentaurHauls cores with conflicting family/model ID. Cc: stable@vger.kernel.org Signed-off-by: Yao Zi Link: https://lore.kernel.org/all/20260228173704.62460-1-me@ziyao.cc/ Signed-off-by: Mingcong Bai --- arch/x86/kernel/cpu/centaur.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/x86/kernel/cpu/centaur.c b/arch/x86/kernel/cpu/centaur.c index 81695da9c52421..3773784ba6a9d7 100644 --- a/arch/x86/kernel/cpu/centaur.c +++ b/arch/x86/kernel/cpu/centaur.c @@ -108,6 +108,29 @@ static void early_init_centaur(struct cpuinfo_x86 *c) } } +/* + * Zhaoxin C4600 (family 6, model 15) names itself as CentaurHauls, it claims + * X86_FEATURE_FSGSBASE support in CPUID, while executing any fsgsbase-related + * instructions on it results in #UD. + */ +static void fixup_zhaoxin_fsgsbase(struct cpuinfo_x86 *c) +{ + const char *name, *model_names[] = { + "C-QuadCore C4600" + }; + int i; + + for (i = 0; i < ARRAY_SIZE(model_names); i++) { + name = model_names[i]; + + if (!strncmp(c->x86_model_id, name, strlen(name))) { + pr_warn_once("CPU has broken FSGSBASE support\n"); + setup_clear_cpu_cap(X86_FEATURE_FSGSBASE); + return; + } + } +} + static void init_centaur(struct cpuinfo_x86 *c) { #ifdef CONFIG_X86_32 @@ -201,6 +224,8 @@ static void init_centaur(struct cpuinfo_x86 *c) set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC); #endif + fixup_zhaoxin_fsgsbase(c); + init_ia32_feat_ctl(c); } From e1ecf4bcf8f6c7263463fc50906073c2b39829e4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 16 Apr 2026 16:48:29 +0200 Subject: [PATCH 142/521] FROMLIST: HID: multitouch: use __free(kfree) to clean up temporary buffers This simplifies error handling and protects against memory leaks. Signed-off-by: Benjamin Tissoires Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- drivers/hid/hid-multitouch.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index b085bed874bf9b..a02ca0bb98efbb 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -506,7 +506,6 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) { int ret; u32 size = hid_report_len(report); - u8 *buf; /* * Do not fetch the feature report if the device has been explicitly @@ -515,7 +514,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) if (hdev->quirks & HID_QUIRK_NO_INIT_REPORTS) return; - buf = hid_alloc_report_buf(report, GFP_KERNEL); + u8 *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) return; @@ -528,7 +527,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) /* The report ID in the request and the response should match */ if (report->id != buf[0]) { hid_err(hdev, "Returned feature report did not match the request\n"); - goto free; + return; } ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf, @@ -536,9 +535,6 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) if (ret) dev_warn(&hdev->dev, "failed to report feature\n"); } - -free: - kfree(buf); } static void mt_feature_mapping(struct hid_device *hdev, @@ -1666,7 +1662,6 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev, struct mt_class *cls = &td->mtclass; struct hid_report *report = field->report; unsigned int index = usage->usage_index; - char *buf; u32 report_len; int max; @@ -1681,17 +1676,18 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev, return false; if (cls->quirks & MT_QUIRK_FORCE_GET_FEATURE) { - report_len = hid_report_len(report); - buf = hid_alloc_report_buf(report, GFP_KERNEL); + char *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) { hid_err(hdev, "failed to allocate buffer for report\n"); return false; } + + report_len = hid_report_len(report); hid_hw_raw_request(hdev, report->id, buf, report_len, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); - kfree(buf); } field->value[index] = td->inputmode_value; From d710d1134788cc2f97793499198f8e2cd1e16a04 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:34 +0100 Subject: [PATCH 143/521] FROMLIST: drm: panel-orientation-quirks: Add AOKZOE A1 Pro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AOKZOE A1 Pro has a portrait 16:10 panel, add a quirk for it. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 3a218fb592cef4..736280302f7613 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -179,6 +179,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Win600"), }, .driver_data = (void *)&lcd720x1280_rightside_up, + }, { /* AOKZOE A1 Pro */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AOKZOE"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AOKZOE A1 Pro"), + }, + .driver_data = (void *)&lcd1200x1920_leftside_up, }, { /* Asus T100HA */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), From ac954720de34a2469c25277bfd113f9d5a85f321 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:35 +0100 Subject: [PATCH 144/521] FROMLIST: drm: panel-orientation-quirks: Add additional ID for Ayaneo 2021 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Ayaneo 2021 has an alternate variant that skips AYA in the beginning. Add that as well. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 736280302f7613..23e736469aab93 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -215,6 +215,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AYA NEO 2021"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* AYA NEO 2021 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "NEO 2021"), + }, + .driver_data = (void *)&lcd800x1280_rightside_up, }, { /* AYA NEO AIR */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYANEO"), From b3123c55e4632a14ad138318ef94f346601a60c6 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:36 +0100 Subject: [PATCH 145/521] FROMLIST: drm: panel-orientation-quirks: Add Ayaneo 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Ayaneo 3 comes with two panels, an OLED right side up 1080p panel and an IPS landscape 1080p panel. However, both have the same DMI data. This quirk adds support for the portrait OLED panel. As the landscape panel is 1920x1080 and the right side up panel is 1080x1920, the width and height arguments are used to differentiate the panels. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 23e736469aab93..f745db95c39464 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -209,6 +209,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_MATCH(DMI_PRODUCT_NAME, "AYANEO 2"), }, .driver_data = (void *)&lcd1200x1920_rightside_up, + }, { /* AYANEO 3 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AYANEO 3"), + }, + .driver_data = (void *)&lcd1080x1920_rightside_up, }, { /* AYA NEO 2021 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYADEVICE"), From 0721304317cadfc822b9e7828a1d9286276f585f Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:37 +0100 Subject: [PATCH 146/521] FROMLIST: drm: panel-orientation-quirks: Add OneXPlayer X1 variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OneXPlayer X1 series features a 2k 10.95 display with a portrait orientation. Add quirks to set the panel orientation to portrait mode to the Intel, AMD, and EVA-02 variants. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- .../gpu/drm/drm_panel_orientation_quirks.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index f745db95c39464..2efffe962460c3 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -504,6 +504,30 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONE XPLAYER"), }, .driver_data = (void *)&lcd1200x1920_leftside_up, + }, { /* OneXPlayer X1 AMD */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1 A"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer X1 Intel */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1 i"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer X1 AMD Strix Point */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1Pro"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer X1Pro EVA variant with Intel */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1Pro EVA-02"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, }, { /* OrangePi Neo */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "OrangePi"), From e9b93fb1806243e52b015f6851242690e3295995 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:38 +0100 Subject: [PATCH 147/521] FROMLIST: drm: panel-orientation-quirks: Add OneXPlayer X1 Mini variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OneXPlayer X1 mini features a 2k 8.8 display with a portrait orientation. The Pro is a CPU refresh. Add quirks to set the panel orientation to portrait mode. There is no Intel variant. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 2efffe962460c3..1ee66e4db0e9ef 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -516,6 +516,18 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1 i"), }, .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer X1 mini (AMD) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1 mini"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer X1 mini pro (AMD Strix Point) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1Mini Pro"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, }, { /* OneXPlayer X1 AMD Strix Point */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), From 51f46701c1efe3d8d00c6e4521ba9bfd4dcd058e Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:39 +0100 Subject: [PATCH 148/521] FROMLIST: drm: panel-orientation-quirks: Add OneXPlayer F1 variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OneXPlayer F1Pro has a 144hz 1920x1080 portrait OLED panel. Add a quirk to correct the panel portrait orientation. In addition, it comes with a red limited edition variant in the Chinese market, so add that as well. Then, add the 8840U non-pro variant as well. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 1ee66e4db0e9ef..af45d79622b5ae 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -540,6 +540,24 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1Pro EVA-02"), }, .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer OneXFly F1 Pro (OLED) LE Red variant */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER F1 EVA-02"), + }, + .driver_data = (void *)&lcd1080x1920_leftside_up, + }, { /* OneXPlayer OneXFly F1 Pro (OLED) Hawk Point */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER F1 OLED"), + }, + .driver_data = (void *)&lcd1080x1920_leftside_up, + }, { /* OneXPlayer OneXFly F1 Pro (OLED) Strix Point */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER F1Pro"), + }, + .driver_data = (void *)&lcd1080x1920_leftside_up, }, { /* OrangePi Neo */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "OrangePi"), From 4c1871f37235e267af5361227babad4f46d0062c Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:40 +0100 Subject: [PATCH 149/521] FROMLIST: drm: panel-orientation-quirks: Add OneXPlayer G1 variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add quirks for the new clamshell device OneXPlayer G1 for both AMD and Intel. The device has a 1600x2560p 144hz LCD panel. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index af45d79622b5ae..675a0aeb750e06 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -558,6 +558,18 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER F1Pro"), }, .driver_data = (void *)&lcd1080x1920_leftside_up, + }, { /* OneXPlayer OneXFly G1 AMD */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 A"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer OneXFly G1 Intel */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 i"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, }, { /* OrangePi Neo */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "OrangePi"), From c63480318a3457ffae20b9d77caecc97134eddbe Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:41 +0100 Subject: [PATCH 150/521] FROMLIST: drm: panel-orientation-quirks: Add GPD Win Max (2021) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right side up, DSI-1, 800x1280 screen. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 675a0aeb750e06..21ae8a811e2bf9 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -327,6 +327,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1619-01"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* GPD Win Max (2021) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1619-03"), + }, + .driver_data = (void *)&lcd800x1280_rightside_up, }, { /* * GPD Pocket, note that the DMI data is less generic then * it seems, devices with a board-vendor of "AMI Corporation" From 22bb2162bba36e869078e74dd65fac2574b20c2f Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:42 +0100 Subject: [PATCH 151/521] FROMLIST: drm: panel-orientation-quirks: Add GPD Pocket 4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GPD Pocket 4 is a mini laptop replacement with a portrait 2k panel. Add a quirk for it. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 21ae8a811e2bf9..8475811df89096 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -388,6 +388,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1617-01") }, .driver_data = (void *)&lcd1080x1920_rightside_up, + }, { /* GPD Pocket 4 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G1628-04"), + }, + .driver_data = (void *)&lcd1600x2560_rightside_up, }, { /* I.T.Works TW891 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), From a2d138b2ebffbc0f0453ec05025eac93b7e5ee57 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:43 +0100 Subject: [PATCH 152/521] FROMLIST: drm: panel-orientation-quirks: Add Zeenix Lite and Pro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add quirks for two Zeenix handhelds, the Lite and the Pro. They are identical to the Ayn Loki and the Ayn Loki Pro respectively. Reviewed-by: Philip Müller Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 8475811df89096..c055be8d8b2564 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -594,6 +594,18 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galaxy Book 10.6"), }, .driver_data = (void *)&lcd1280x1920_rightside_up, + }, { /* Tectoy Zeenix Lite */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Tectoy"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Zeenix Lite"), + }, + .driver_data = (void *)&lcd1080x1920_leftside_up, + }, { /* Tectoy Zeenix Pro */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Tectoy"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Zeenix Pro"), + }, + .driver_data = (void *)&lcd1080x1920_leftside_up, }, { /* Valve Steam Deck (Jupiter) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"), From 2ae892e7a079c11fcb1fd481114a7bc046d851ed Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:44 +0100 Subject: [PATCH 153/521] FROMLIST: drm: panel-orientation-quirks: add SuiPlay0X1 Very similar to the AYANEO 2S, the Mysten SuiPlay0X1 is a handheld gaming device with a 1200x1920 display that is mounted in a right-side-up orientation. Add a quirk for it. Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index c055be8d8b2564..857fbc1ba4dcfd 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -491,6 +491,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), }, .driver_data = (void *)&lcd1600x2560_rightside_up, + }, { /* Mysten SuiPlay0X1 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Mysten Labs, Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SuiPlay0X1"), + }, + .driver_data = (void *)&lcd1200x1920_rightside_up, }, { /* Nanote UMPC-01 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "RWC CO.,LTD"), From 4afbf118dc78c4b5dbf7c2f0a543da69d89c2497 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:45 +0100 Subject: [PATCH 154/521] FROMLIST: drm: panel-orientation-quirks: Add OneXPlayer X1z Different variant of the OneXPlayer X1 AMD edition with an 8840U. Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 857fbc1ba4dcfd..96c910d4524e7a 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -546,6 +546,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1Mini Pro"), }, .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer X1 AMD second variant */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1z"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, }, { /* OneXPlayer X1 AMD Strix Point */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), From 660bf7288abd78398cf4d00476152cd6d7e48e2b Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:46 +0100 Subject: [PATCH 155/521] FROMLIST: drm: panel-orientation-quirks: Add AOKZOE A2 Pro We are missing a quirk for this older device, so add it. Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 96c910d4524e7a..6de137b5e77748 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -185,6 +185,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AOKZOE A1 Pro"), }, .driver_data = (void *)&lcd1200x1920_leftside_up, + }, { /* AOKZOE A2 Pro */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AOKZOE"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AOKZOE A2 Pro"), + }, + .driver_data = (void *)&lcd1200x1920_leftside_up, }, { /* Asus T100HA */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), From 91b4bf2ac845f0042b8158aa77b57d1c6d329585 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Mon, 16 Feb 2026 21:45:47 +0100 Subject: [PATCH 156/521] FROMLIST: drm: panel-orientation-quirks: Add OneXPlayer X1 Air Special edition of the X1 with a new Intel chipset. Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/all/20260216204547.293291-1-lkml@antheas.dev/ Signed-off-by: CyberNeko.AI Signed-off-by: Mingcong Bai --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 6de137b5e77748..b1915c1f92da0c 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -546,6 +546,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1 mini"), }, .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* OneXPlayer X1 Intel */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X1Air"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, }, { /* OneXPlayer X1 mini pro (AMD Strix Point) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), From 14f5d9b284d3babeb51e0694d6fe29b7f88c9872 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 14 Apr 2026 14:25:10 +0800 Subject: [PATCH 157/521] FROMLIST: dt-bindings: i2c: loongson,ls2x: Add ls2k0300-i2c compatible Add "loongson,ls2k0300-i2c" dedicated compatible for representing I2C of Loongson-2K0300 chip, because its HW integration is quiet different from others. Acked-by: Conor Dooley Reviewed-by: Huacai Chen Signed-off-by: Binbin Zhou Link: https://lore.kernel.org/loongarch/cover.1776135865.git.zhoubinbin@loongson.cn/ Signed-off-by: Mingcong Bai --- Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml index 67882ec6e06afc..ee09c6d9c5f0b7 100644 --- a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml @@ -16,6 +16,7 @@ properties: compatible: enum: - loongson,ls2k-i2c + - loongson,ls2k0300-i2c - loongson,ls7a-i2c reg: @@ -24,6 +25,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + required: - compatible - reg From 2235b4a19c9565fc7c5a61bc980fb5205e43ab04 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 14 Apr 2026 14:25:11 +0800 Subject: [PATCH 158/521] FROMLIST: i2c: ls2x-v2: Add driver for Loongson-2K0300 I2C controller This I2C module is integrated into the Loongson-2K0300 SoCs. It provides multi-master functionality and controls all I2C bus-specific timing, protocols, arbitration, and timing. It supports both standard and fast modes. Reviewed-by: Andy Shevchenko Reviewed-by: Huacai Chen Signed-off-by: Binbin Zhou Link: https://lore.kernel.org/loongarch/cover.1776135865.git.zhoubinbin@loongson.cn/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 1 + drivers/i2c/busses/Kconfig | 11 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ls2x-v2.c | 544 +++++++++++++++++++++++++++++++ 4 files changed, 557 insertions(+) create mode 100644 drivers/i2c/busses/i2c-ls2x-v2.c diff --git a/MAINTAINERS b/MAINTAINERS index f74da642290aa6..b70033b1e19ff0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15073,6 +15073,7 @@ M: Binbin Zhou L: linux-i2c@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml +F: drivers/i2c/busses/i2c-ls2x-v2.c F: drivers/i2c/busses/i2c-ls2x.c LOONGSON PWM DRIVER diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bfe191e50b49a2..30cb64ed431092 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -851,6 +851,17 @@ config I2C_LS2X This driver can also be built as a module. If so, the module will be called i2c-ls2x. +config I2C_LS2X_V2 + tristate "Loongson-2 Fast Speed I2C adapter" + depends on LOONGARCH || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes to this option, support will be included for the + I2C interface on the Loongson-2K0300 SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-ls2x-v2. + config I2C_MLXBF tristate "Mellanox BlueField I2C controller" depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 547123ab351f10..3755c54b3d8284 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_I2C_KEBA) += i2c-keba.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o +obj-$(CONFIG_I2C_LS2X_V2) += i2c-ls2x-v2.o obj-$(CONFIG_I2C_MESON) += i2c-meson.o obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o diff --git a/drivers/i2c/busses/i2c-ls2x-v2.c b/drivers/i2c/busses/i2c-ls2x-v2.c new file mode 100644 index 00000000000000..517760d7016905 --- /dev/null +++ b/drivers/i2c/busses/i2c-ls2x-v2.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Loongson-2K0300 I2C controller driver + * + * Copyright (C) 2025-2026 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Loongson-2 fast I2C offset registers */ +#define LOONGSON2_I2C_CR1 0x00 /* I2C control 1 register */ +#define LOONGSON2_I2C_CR2 0x04 /* I2C control 2 register */ +#define LOONGSON2_I2C_OAR 0x08 /* I2C slave address register */ +#define LOONGSON2_I2C_DR 0x10 /* I2C data register */ +#define LOONGSON2_I2C_SR1 0x14 /* I2C status 1 register */ +#define LOONGSON2_I2C_SR2 0x18 /* I2C status 2 register */ +#define LOONGSON2_I2C_CCR 0x1c /* I2C clock control register */ +#define LOONGSON2_I2C_TRISE 0x20 /* I2C trise register */ +#define LOONGSON2_I2C_FLTR 0x24 + +/* Bitfields of I2C control 1 register */ +#define LOONGSON2_I2C_CR1_PE BIT(0) /* Peripheral enable */ +#define LOONGSON2_I2C_CR1_START BIT(8) /* Start generation */ +#define LOONGSON2_I2C_CR1_STOP BIT(9) /* Stop generation */ +#define LOONGSON2_I2C_CR1_ACK BIT(10) /* Acknowledge enable */ +#define LOONGSON2_I2C_CR1_POS BIT(11) /* Acknowledge/PEC Position (for data reception) */ + +#define LOONGSON2_I2C_CR1_OP_MASK (LOONGSON2_I2C_CR1_START | LOONGSON2_I2C_CR1_STOP) + +/* Bitfields of I2C control 2 register */ +#define LOONGSON2_I2C_CR2_FREQ GENMASK(5, 0) /* APB Clock Frequency in MHz */ +#define LOONGSON2_I2C_CR2_ITERREN BIT(8) /* Fault-Class Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITEVTEN BIT(9) /* Event-Based Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITBUFEN BIT(10) /* Cache-Class Interrupt Enable */ + +#define LOONGSON2_I2C_CR2_INT_MASK \ + (LOONGSON2_I2C_CR2_ITBUFEN | LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN) + +/* Bitfields of I2C status 1 register */ +#define LOONGSON2_I2C_SR1_SB BIT(0) /* Start bit (Master mode) */ +#define LOONGSON2_I2C_SR1_ADDR BIT(1) /* Address sent (master mode) */ +#define LOONGSON2_I2C_SR1_BTF BIT(2) /* Byte transfer finished */ +#define LOONGSON2_I2C_SR1_RXNE BIT(6) /* Data register not empty (receivers) */ +#define LOONGSON2_I2C_SR1_TXE BIT(7) /* Data register empty (transmitters) */ +#define LOONGSON2_I2C_SR1_BERR BIT(8) /* Bus error */ +#define LOONGSON2_I2C_SR1_ARLO BIT(9) /* Arbitration lost (master mode) */ +#define LOONGSON2_I2C_SR1_AF BIT(10) /* Acknowledge failure */ + +#define LOONGSON2_I2C_SR1_ITEVTEN_MASK \ + (LOONGSON2_I2C_SR1_BTF | LOONGSON2_I2C_SR1_ADDR | LOONGSON2_I2C_SR1_SB) +#define LOONGSON2_I2C_SR1_ITBUFEN_MASK (LOONGSON2_I2C_SR1_TXE | LOONGSON2_I2C_SR1_RXNE) +#define LOONGSON2_I2C_SR1_ITERREN_MASK \ + (LOONGSON2_I2C_SR1_AF | LOONGSON2_I2C_SR1_ARLO | LOONGSON2_I2C_SR1_BERR) + +/* Bitfields of I2C status 2 register */ +#define LOONGSON2_I2C_SR2_MSL BIT(0) /* Master/slave */ +#define LOONGSON2_I2C_SR2_BUSY BIT(1) /* Bus busy */ +#define LOONGSON2_I2C_SR2_TRA BIT(2) /* Transmitter/receiver */ +#define LOONGSON2_I2C_SR2_GENCALL BIT(4) /* General call address (Slave mode) */ + +/* Bitfields of I2C clock control register */ +#define LOONGSON2_I2C_CCR_CCR GENMASK(11, 0) +#define LOONGSON2_I2C_CCR_DUTY BIT(14) +#define LOONGSON2_I2C_CCR_FS BIT(15) + +/* Bitfields of I2C trise register */ +#define LOONGSON2_I2C_TRISE_SCL GENMASK(5, 0) + +#define LOONGSON2_I2C_FREE_SLEEP_US 10 +#define LOONGSON2_I2C_FREE_TIMEOUT_US (2 * USEC_PER_MSEC) + +/** + * struct loongson2_i2c_msg - client specific data + * @buf: data buffer + * @count: number of bytes to be transferred + * @result: result of the transfer + * @addr: 8-bit slave addr, including r/w bit + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct loongson2_i2c_msg { + u8 *buf; + u32 count; + int result; + u8 addr; + bool stop; +}; + +/** + * struct loongson2_i2c_priv - private data of the controller + * @adapter: I2C adapter for this controller + * @complete: completion of I2C message + * @clk: hw i2c clock + * @regmap: regmap of the I2C device + * @parent_rate_MHz: I2C clock parent rate + * @msg: I2C transfer information + */ +struct loongson2_i2c_priv { + struct i2c_adapter adapter; + struct completion complete; + struct clk *clk; + struct regmap *regmap; + unsigned long parent_rate_MHz; + struct loongson2_i2c_msg msg; +}; + +static void loongson2_i2c_disable_irq(struct loongson2_i2c_priv *priv) +{ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_INT_MASK, 0); +} + +static void loongson2_i2c_read_msg(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + u32 rbuf; + + regmap_read(priv->regmap, LOONGSON2_I2C_DR, &rbuf); + *msg->buf++ = rbuf; + msg->count--; +} + +static void loongson2_i2c_write_msg(struct loongson2_i2c_priv *priv, u8 byte) +{ + regmap_write(priv->regmap, LOONGSON2_I2C_DR, byte); +} + +static void loongson2_i2c_terminate_xfer(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + loongson2_i2c_disable_irq(priv); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_write(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + if (msg->count) { + loongson2_i2c_write_msg(priv, *msg->buf++); + if (!--msg->count) + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITBUFEN, 0); + } else { + loongson2_i2c_terminate_xfer(priv); + } +} + +static void loongson2_i2c_handle_rx_addr(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 0: + loongson2_i2c_terminate_xfer(priv); + break; + case 1: + /* Enable NACK and reset POS (Acknowledge position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, + LOONGSON2_I2C_CR1_ACK | LOONGSON2_I2C_CR1_POS, 0); + /* Set STOP or RepSTART */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + break; + case 2: + /* Enable NACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + /* Set POS (NACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, + LOONGSON2_I2C_CR1_POS); + break; + + default: + /* Enable ACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, + LOONGSON2_I2C_CR1_ACK); + /* Reset POS (ACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, 0); + break; + } +} + +static void loongson2_i2c_isr_error(u32 status, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct loongson2_i2c_msg *msg = &priv->msg; + + /* Arbitration lost */ + if (status & LOONGSON2_I2C_SR1_ARLO) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_ARLO, 0); + msg->result = -EAGAIN; + goto out; + } + + /* + * Acknowledge failure: + * In master transmitter mode a Stop must be generated by software. + */ + if (status & LOONGSON2_I2C_SR1_AF) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_STOP, + LOONGSON2_I2C_CR1_STOP); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_AF, 0); + msg->result = -EIO; + goto out; + } + + /* Bus error */ + if (status & LOONGSON2_I2C_SR1_BERR) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_BERR, 0); + msg->result = -EIO; + goto out; + } + +out: + loongson2_i2c_disable_irq(priv); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_read(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 1: + loongson2_i2c_disable_irq(priv); + loongson2_i2c_read_msg(priv); + complete(&priv->complete); + break; + case 2: + case 3: + /* + * For 2-byte/3-byte reception and for N-byte reception with N > 3, we have to + * wait for byte transferred finished event before reading data. + * Just disable buffer interrupt in order to avoid another system preemption due + * to RX not empty event. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, 0); + break; + default: + /* + * For N byte reception with N > 3 we directly read data register + * until N-2 data. + */ + loongson2_i2c_read_msg(priv); + break; + } +} + +static void loongson2_i2c_handle_rx_done(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 2: + /* + * The STOP/START bit has to be set before reading the last two bytes. + * After that, we could read the last two bytes. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + + for (unsigned int i = msg->count; i > 0; i--) + loongson2_i2c_read_msg(priv); + + loongson2_i2c_disable_irq(priv); + + complete(&priv->complete); + break; + case 3: + /* + * In order to generate the NACK after the last received data byte, enable NACK + * before reading N-2 data. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + loongson2_i2c_read_msg(priv); + break; + default: + loongson2_i2c_read_msg(priv); + break; + } +} + +static irqreturn_t loongson2_i2c_isr_event(int irq, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct device *dev = regmap_get_device(priv->regmap); + struct loongson2_i2c_msg *msg = &priv->msg; + u32 status, ien, event, cr2, possible_status; + + regmap_read(priv->regmap, LOONGSON2_I2C_SR1, &status); + if (status & LOONGSON2_I2C_SR1_ITERREN_MASK) { + loongson2_i2c_isr_error(status, data); + return IRQ_NONE; + } + + regmap_read(priv->regmap, LOONGSON2_I2C_CR2, &cr2); + ien = cr2 & LOONGSON2_I2C_CR2_INT_MASK; + + /* Update possible_status if buffer interrupt is enabled */ + possible_status = LOONGSON2_I2C_SR1_ITEVTEN_MASK; + if (ien & LOONGSON2_I2C_CR2_ITBUFEN) + possible_status |= LOONGSON2_I2C_SR1_ITBUFEN_MASK; + + event = status & possible_status; + if (!event) { + dev_dbg(dev, "spurious evt IRQ (status=0x%08x, ien=0x%08x)\n", status, ien); + return IRQ_NONE; + } + + /* Start condition generated */ + if (event & LOONGSON2_I2C_SR1_SB) + loongson2_i2c_write_msg(priv, msg->addr); + + /* I2C Address sent */ + if (event & LOONGSON2_I2C_SR1_ADDR) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_addr(priv); + /* Clear ADDR flag */ + regmap_read(priv->regmap, LOONGSON2_I2C_SR2, &status); + /* Enable buffer interrupts for RX/TX not empty events */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, + LOONGSON2_I2C_CR2_ITBUFEN); + } + + /* TX empty */ + if ((event & LOONGSON2_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD)) + loongson2_i2c_handle_write(priv); + + /* RX not empty */ + if ((event & LOONGSON2_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD)) + loongson2_i2c_handle_read(priv); + + /* + * The BTF (Byte Transfer finished) event occurs when: + * - in reception: a new byte is received in the shift register + * but the previous byte has not been read yet from data register + * - in transmission: a new byte should be sent but the data register + * has not been written yet + */ + if (event & LOONGSON2_I2C_SR1_BTF) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_done(priv); + else + loongson2_i2c_handle_write(priv); + } + + return IRQ_HANDLED; +} + +static int loongson2_i2c_xfer_msg(struct loongson2_i2c_priv *priv, struct i2c_msg *msg, + bool is_stop) +{ + struct loongson2_i2c_msg *l_msg = &priv->msg; + unsigned long timeout; + + l_msg->addr = i2c_8bit_addr_from_msg(msg); + l_msg->buf = msg->buf; + l_msg->count = msg->len; + l_msg->stop = is_stop; + l_msg->result = 0; + + reinit_completion(&priv->complete); + + /* Enable events and errors interrupts */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN); + + timeout = wait_for_completion_timeout(&priv->complete, priv->adapter.timeout); + if (!timeout) + return -ETIMEDOUT; + + return l_msg->result; +} + +static int loongson2_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct loongson2_i2c_priv *priv = i2c_get_adapdata(i2c_adap); + struct device *dev = regmap_get_device(priv->regmap); + unsigned int status; + int ret; + + /* Wait I2C bus free */ + ret = regmap_read_poll_timeout(priv->regmap, LOONGSON2_I2C_SR2, status, + !(status & LOONGSON2_I2C_SR2_BUSY), + LOONGSON2_I2C_FREE_SLEEP_US, + LOONGSON2_I2C_FREE_TIMEOUT_US); + if (ret) { + dev_dbg(dev, "The I2C bus is busy now.\n"); + return ret; + } + + /* Start generation */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_START, + LOONGSON2_I2C_CR1_START); + + for (unsigned int i = 0; i < num; i++) { + ret = loongson2_i2c_xfer_msg(priv, &msgs[i], i == num - 1); + if (ret < 0) + return ret; + } + + return num; +} + +static u32 loongson2_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm loongson2_i2c_algo = { + .xfer = loongson2_i2c_xfer, + .functionality = loongson2_i2c_func, +}; + +static int loongson2_i2c_adjust_bus_speed(struct loongson2_i2c_priv *priv) +{ + struct device *dev = regmap_get_device(priv->regmap); + struct i2c_timings i2c_t; + u32 val, freq_MHz, ccr; + + i2c_parse_fw_timings(dev, &i2c_t, true); + priv->parent_rate_MHz = clk_get_rate(priv->clk); + + if (i2c_t.bus_freq_hz == I2C_MAX_STANDARD_MODE_FREQ) { + /* Select Standard mode */ + ccr = 0; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 2); + } else if (i2c_t.bus_freq_hz == I2C_MAX_FAST_MODE_FREQ) { + /* Select Fast mode */ + ccr = LOONGSON2_I2C_CCR_FS; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 3); + } else { + return dev_err_probe(dev, -EINVAL, "Unsupported speed (%uHz)\n", i2c_t.bus_freq_hz); + } + + FIELD_MODIFY(LOONGSON2_I2C_CCR_CCR, &ccr, val); + regmap_write(priv->regmap, LOONGSON2_I2C_CCR, ccr); + + freq_MHz = DIV_ROUND_UP(priv->parent_rate_MHz, HZ_PER_MHZ); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_FREQ, + FIELD_GET(LOONGSON2_I2C_CR2_FREQ, freq_MHz)); + + regmap_update_bits(priv->regmap, LOONGSON2_I2C_TRISE, LOONGSON2_I2C_TRISE_SCL, + LOONGSON2_I2C_TRISE_SCL); + + /* Enable I2C */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_PE, + LOONGSON2_I2C_CR1_PE); + + return 0; +} + +static const struct regmap_config loongson2_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_I2C_TRISE, +}; + +static int loongson2_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson2_i2c_priv *priv; + struct i2c_adapter *adap; + void __iomem *base; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, &loongson2_i2c_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), "Failed to init regmap.\n"); + + priv->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock.\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adap = &priv->adapter; + adap->retries = 5; + adap->nr = pdev->id; + adap->dev.parent = dev; + adap->owner = THIS_MODULE; + adap->algo = &loongson2_i2c_algo; + adap->timeout = 2 * HZ; + device_set_node(&adap->dev, dev_fwnode(dev)); + i2c_set_adapdata(adap, priv); + strscpy(adap->name, pdev->name); + init_completion(&priv->complete); + platform_set_drvdata(pdev, priv); + + ret = loongson2_i2c_adjust_bus_speed(priv); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, loongson2_i2c_isr_event, IRQF_SHARED, pdev->name, priv); + if (ret) + return ret; + + return devm_i2c_add_adapter(dev, adap); +} + +static const struct of_device_id loongson2_i2c_id_table[] = { + { .compatible = "loongson,ls2k0300-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, loongson2_i2c_id_table); + +static struct platform_driver loongson2_i2c_driver = { + .driver = { + .name = "loongson2-i2c-v2", + .of_match_table = loongson2_i2c_id_table, + }, + .probe = loongson2_i2c_probe, +}; +module_platform_driver(loongson2_i2c_driver); + +MODULE_DESCRIPTION("Loongson-2K0300 I2C bus driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); From eb20c62e463854d0d53a58d97a375c419e067dc3 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Thu, 9 Apr 2026 20:19:34 +0800 Subject: [PATCH 159/521] BACKPORT: FROMLIST: LoongArch: detect and disable sc.q if erratic We've observed that, on some Loongson 2K3000/3B6000M systems with earlier firmware revisions, the sc.q instruction may write incorrect data into the upper half of the written 128-bit datum. It seems upgrading the firmware (for example, the 202602 release from Loongson [1]) will resolve the issue. But since not all systems may be running the most up-to-date firmware, based on firmware update avail- ability and the environment in which they are running in. To help with system compatibility and ensure correct behavior, check if sc.q behaves erratically and disable if so. Link: https://github.com/loongson/Firmware/pull/156 [1] Signed-off-by: Xi Ruoyao Link: https://lore.kernel.org/loongarch/20260409121936.871418-1-xry111@xry111.site/ Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in arch/loongarch/kernel/cpu-probe.c ] Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/cpu-probe.c | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c index 74d31f260dfd88..c256b1f062bf4c 100644 --- a/arch/loongarch/kernel/cpu-probe.c +++ b/arch/loongarch/kernel/cpu-probe.c @@ -133,6 +133,43 @@ static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa) } } +/* + * Some LoongArch has broken sc.q which incorrectly handles the upper word + * when the lower word is zero. Newer firmware versions (such as the 202602 + * release from Loongson) seem to contain a workaround for this issue. + * + * Disable sc.q if erratic to ensure reliability and compatibility. + */ +static bool sc_q_is_sane(void) +{ + struct { + long word[2]; + } __aligned(16) mem; + register long tmp asm("t0"); + register long one asm("t1") = 1; + register long *ptr asm("t2") = &mem.word[0]; + + /* + * The sc.q instruction is hard coded with .word so the HWCAP bit + * exported to the userspace won't depend on the assembler version + * used to build the kernel. + */ + asm ( + "1:ll.d\t$r0, %[ptr], 0\n\t" + "move\t%[tmp], $r0\n\t" + ".word\t0x385735cc\n\t" /* sc.q %[tmp], %[one], %[ptr]*/ + "beqz\t%[tmp], 1b" + : [tmp] "=&r" (tmp), "=m" (mem) + : [ptr] "r" (ptr), [one] "r" (one)); + + if (mem.word[1] != 1) { + pr_warn_once("Warning: sc.q is erratic on this platform, disabling for both kernel and HWCAP. Please try a firmware update."); + return false; + } + + return true; +} + static void cpu_probe_common(struct cpuinfo_loongarch *c) { unsigned int config; @@ -182,7 +219,7 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) c->options |= LOONGARCH_CPU_LAM_BH; elf_hwcap |= HWCAP_LOONGARCH_LAM_BH; } - if (config & CPUCFG2_SCQ) { + if ((config & CPUCFG2_SCQ) && sc_q_is_sane()) { c->options |= LOONGARCH_CPU_SCQ; elf_hwcap |= HWCAP_LOONGARCH_SCQ; } From 7fa6bb7af76da87435ace688c17ea2f5193918dc Mon Sep 17 00:00:00 2001 From: Hongliang Wang Date: Mon, 8 Jun 2026 10:45:32 +0800 Subject: [PATCH 160/521] FROMLIST: dt-bindings: i2c: ls2x-i2c: Add clocks and clock-frequency properties Add clocks and clock-frequency properties to examples. Reviewed-by: Huacai Chen Acked-by: Conor Dooley Signed-off-by: Hongliang Wang Link: https://lore.kernel.org/loongarch/20260608024533.32419-1-wanghongliang@loongson.cn/ Signed-off-by: Mingcong Bai --- Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml index ee09c6d9c5f0b7..0beb7f2515c8cb 100644 --- a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml @@ -37,11 +37,14 @@ unevaluatedProperties: false examples: - | + #include #include i2c0: i2c@1fe21000 { compatible = "loongson,ls2k-i2c"; reg = <0x1fe21000 0x8>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&extioiic>; interrupts = <22 IRQ_TYPE_LEVEL_LOW>; #address-cells = <1>; From 6298962e46871c29d32260d503c8be5158bb9849 Mon Sep 17 00:00:00 2001 From: Hongliang Wang Date: Mon, 8 Jun 2026 10:45:33 +0800 Subject: [PATCH 161/521] FROMLIST: i2c: ls2x: Add clocks property parsing and adjust bus speed The i2c-ls2x driver supports dts and acpi parameter passing. In dts, uses clock framework, by parsing clocks property to get i2c bus reference clock, and define the div of reference clock by device data. In acpi, by passing clocks property to describe i2c bus reference clock and clock-div property to describe the div of reference clock. Based on i2c bus reference clock(clock_a), i2c bus speed(clock_s) and div, calculate the prcescale of i2c divider register. The calculation formula is prcescale = (clock_a*10)/(div*clock_s)-1 Reviewed-by: Huacai Chen Cc: stable@vger.kernel.org Signed-off-by: Hongliang Wang Link: https://lore.kernel.org/loongarch/20260608024533.32419-1-wanghongliang@loongson.cn/ Signed-off-by: Mingcong Bai --- drivers/i2c/busses/i2c-ls2x.c | 36 ++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.c index b475dd27b7af94..46dafa11b3013a 100644 --- a/drivers/i2c/busses/i2c-ls2x.c +++ b/drivers/i2c/busses/i2c-ls2x.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -63,11 +64,18 @@ /* The default bus frequency, which is an empirical value */ #define LS2X_I2C_FREQ_STD (33 * HZ_PER_KHZ) +/* The div of i2c reference clock on LS2K0500/2K1000/2K2000 */ +#define LS2X_I2C_2K_CLOCK_DIV 40 + +/* The div of i2c reference clock on LS7A1000/7A2000 */ +#define LS2X_I2C_7A_CLOCK_DIV 50 + struct ls2x_i2c_priv { struct i2c_adapter adapter; void __iomem *base; struct i2c_timings i2c_t; struct completion cmd_complete; + unsigned int div; }; /* @@ -96,6 +104,8 @@ static irqreturn_t ls2x_i2c_isr(int this_irq, void *dev_id) static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv) { u16 val; + u32 pclk, div; + struct clk *clk; struct i2c_timings *t = &priv->i2c_t; struct device *dev = priv->adapter.dev.parent; u32 acpi_speed = i2c_acpi_find_bus_speed(dev); @@ -107,12 +117,30 @@ static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv) else t->bus_freq_hz = LS2X_I2C_FREQ_STD; + if (dev_of_node(dev)) { + clk = devm_clk_get_optional_enabled(dev, NULL); + if (!IS_ERR_OR_NULL(clk)) + pclk = clk_get_rate(clk); + else + pclk = LS2X_I2C_PCLK_FREQ; + + div = priv->div; + + val = (pclk * 10) / (div * t->bus_freq_hz) - 1; + } else { + /* clocks and clock-div are only ACPI properties. */ + if (!device_property_read_u32(dev, "clocks", &pclk) && + !device_property_read_u32(dev, "clock-div", &div)) + val = (pclk * 10) / (div * t->bus_freq_hz) - 1; + else + val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1; + } + /* * According to the chip manual, we can only access the registers as bytes, * otherwise the high bits will be truncated. * So set the I2C frequency with a sequential writeb() instead of writew(). */ - val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1; writeb(FIELD_GET(GENMASK(7, 0), val), priv->base + I2C_LS2X_PRER_LO); writeb(FIELD_GET(GENMASK(15, 8), val), priv->base + I2C_LS2X_PRER_HI); } @@ -295,6 +323,8 @@ static int ls2x_i2c_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->div = (unsigned long)device_get_match_data(dev); + /* Map hardware registers */ priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) @@ -349,8 +379,8 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ls2x_i2c_pm_ops, ls2x_i2c_suspend, ls2x_i2c_resume, NULL); static const struct of_device_id ls2x_i2c_id_table[] = { - { .compatible = "loongson,ls2k-i2c" }, - { .compatible = "loongson,ls7a-i2c" }, + { .compatible = "loongson,ls2k-i2c", .data = (void *)LS2X_I2C_2K_CLOCK_DIV, }, + { .compatible = "loongson,ls7a-i2c", .data = (void *)LS2X_I2C_7A_CLOCK_DIV, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table); From 15d3fa826cbaf4c6357d07d8571502c1cc9cd432 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Mon, 22 Mar 2021 01:11:21 -0600 Subject: [PATCH 162/521] FROMLIST: iwlwifi: add new pci id for 6235 lspci output: Network controller [0280]: Intel Corporation Centrino Advanced-N6235 [8086:088f] (rev 24) Subsystem: Intel Corporation Centrino Advanced-N 6235 [8086:526a] Cc: stable@vger.kernel.org Signed-off-by: Alex Hung [ Mingcong Bai: Fixed a minor merge conflict in drivers/net/wireless/intel/iwlwifi/pcie/drv.c Also found amongst Ubuntu "SAUCE" patches, this patch was acked during the 5.13 cycle but was never merged. ] Link: https://lore.kernel.org/all/20210322071121.265584-1-alex.hung@canonical.com/ Signed-off-by: Mingcong Bai --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index eb3c5a6dd08841..3af3a1641a7035 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -208,6 +208,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6030_mac_cfg)}, {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6030_mac_cfg)}, {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x526A, iwl6030_mac_cfg)}, /* 105 Series */ {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_mac_cfg)}, From c6b541cd4ebb6317f2a32f6a064d04298ee57b2e Mon Sep 17 00:00:00 2001 From: Bin Du Date: Fri, 20 Mar 2026 16:41:40 +0800 Subject: [PATCH 163/521] FROMLIST: media: platform: amd: Introduce amd isp4 capture driver AMD isp4 capture is a v4l2 media device which implements media controller interface. It has one sub-device (AMD ISP4 sub-device) endpoint which can be connected to a remote CSI2 TX endpoint. It supports only one physical interface for now. Also add ISP4 driver related entry info into the MAINTAINERS file Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Link: https://lore.kernel.org/all/20260320084146.200988-1-Bin.Du@amd.com/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 13 +++ drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/amd/Kconfig | 3 + drivers/media/platform/amd/Makefile | 3 + drivers/media/platform/amd/isp4/Kconfig | 15 +++ drivers/media/platform/amd/isp4/Makefile | 6 ++ drivers/media/platform/amd/isp4/isp4.c | 127 +++++++++++++++++++++++ drivers/media/platform/amd/isp4/isp4.h | 17 +++ 9 files changed, 186 insertions(+) create mode 100644 drivers/media/platform/amd/Kconfig create mode 100644 drivers/media/platform/amd/Makefile create mode 100644 drivers/media/platform/amd/isp4/Kconfig create mode 100644 drivers/media/platform/amd/isp4/Makefile create mode 100644 drivers/media/platform/amd/isp4/isp4.c create mode 100644 drivers/media/platform/amd/isp4/isp4.h diff --git a/MAINTAINERS b/MAINTAINERS index b70033b1e19ff0..afc2698456a414 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1159,6 +1159,19 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git F: drivers/iommu/amd/ F: include/linux/amd-iommu.h +AMD ISP4 DRIVER +M: Bin Du +M: Nirujogi Pratap +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: drivers/media/platform/amd/Kconfig +F: drivers/media/platform/amd/Makefile +F: drivers/media/platform/amd/isp4/Kconfig +F: drivers/media/platform/amd/isp4/Makefile +F: drivers/media/platform/amd/isp4/isp4.c +F: drivers/media/platform/amd/isp4/isp4.h + AMD KFD M: Felix Kuehling L: amd-gfx@lists.freedesktop.org diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3f0b7bb68cc940..0b33e927bd59cb 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -63,6 +63,7 @@ config VIDEO_MUX # Platform drivers - Please keep it alphabetically sorted source "drivers/media/platform/allegro-dvt/Kconfig" +source "drivers/media/platform/amd/Kconfig" source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/arm/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6d5f79ddfcc37a..16c185752474e5 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -6,6 +6,7 @@ # Place here, alphabetically sorted by directory # (e. g. LC_ALL=C sort Makefile) obj-y += allegro-dvt/ +obj-y += amd/ obj-y += amlogic/ obj-y += amphion/ obj-y += arm/ diff --git a/drivers/media/platform/amd/Kconfig b/drivers/media/platform/amd/Kconfig new file mode 100644 index 00000000000000..25af49f246b260 --- /dev/null +++ b/drivers/media/platform/amd/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +source "drivers/media/platform/amd/isp4/Kconfig" diff --git a/drivers/media/platform/amd/Makefile b/drivers/media/platform/amd/Makefile new file mode 100644 index 00000000000000..8bfc1955f22e69 --- /dev/null +++ b/drivers/media/platform/amd/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += isp4/ diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig new file mode 100644 index 00000000000000..55dd2dc453a208 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ + +config VIDEO_AMD_ISP4_CAPTURE + tristate "AMD ISP4 and camera driver" + depends on DRM_AMD_ISP && VIDEO_DEV && HAS_DMA + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + select VIDEOBUF2_V4L2 + select VIDEOBUF2_VMALLOC + select VIDEO_V4L2_SUBDEV_API + help + This is support for AMD ISP4 and camera subsystem driver. + Say Y here to enable the ISP4 and camera device for video capture. + To compile this driver as a module, choose M here. The module will + be called amd_isp4_capture. diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile new file mode 100644 index 00000000000000..500b81ce5d1473 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2025 Advanced Micro Devices, Inc. + +obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o +amd_isp4_capture-objs := isp4.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c new file mode 100644 index 00000000000000..58b21258b6d351 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "isp4.h" + +#define ISP4_DRV_NAME "amd_isp_capture" + +static const struct { + const char *name; + u32 status_mask; + u32 en_mask; + u32 ack_mask; + u32 rb_int_num; +} isp4_irq[] = { + /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */ + { + .name = "isp_irq_global", + .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ + }, + { + .name = "isp_irq_stream1", + .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ + }, +}; + +static irqreturn_t isp4_irq_handler(int irq, void *arg) +{ + return IRQ_HANDLED; +} + +static int isp4_capture_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq[ARRAY_SIZE(isp4_irq)]; + struct isp4_device *isp_dev; + int ret; + + isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL); + if (!isp_dev) + return -ENOMEM; + + dev->init_name = ISP4_DRV_NAME; + + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num); + if (irq[i] < 0) + return dev_err_probe(dev, irq[i], + "fail to get irq %d\n", + isp4_irq[i].rb_int_num); + + ret = devm_request_irq(dev, irq[i], isp4_irq_handler, + IRQF_NO_AUTOEN, isp4_irq[i].name, dev); + if (ret) + return dev_err_probe(dev, ret, "fail to req irq %d\n", + irq[i]); + } + + isp_dev->v4l2_dev.mdev = &isp_dev->mdev; + + strscpy(isp_dev->mdev.model, "amd_isp41_mdev", + sizeof(isp_dev->mdev.model)); + isp_dev->mdev.dev = dev; + media_device_init(&isp_dev->mdev); + + snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name), + "AMD-V4L2-ROOT"); + ret = v4l2_device_register(dev, &isp_dev->v4l2_dev); + if (ret) { + dev_err_probe(dev, ret, "fail register v4l2 device\n"); + goto err_clean_media; + } + + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + ret = media_device_register(&isp_dev->mdev); + if (ret) { + dev_err_probe(dev, ret, "fail to register media device\n"); + goto err_isp4_deinit; + } + + platform_set_drvdata(pdev, isp_dev); + + return 0; + +err_isp4_deinit: + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); +err_clean_media: + media_device_cleanup(&isp_dev->mdev); + + return ret; +} + +static void isp4_capture_remove(struct platform_device *pdev) +{ + struct isp4_device *isp_dev = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + media_device_unregister(&isp_dev->mdev); + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); + media_device_cleanup(&isp_dev->mdev); +} + +static struct platform_driver isp4_capture_drv = { + .probe = isp4_capture_probe, + .remove = isp4_capture_remove, + .driver = { + .name = ISP4_DRV_NAME, + } +}; + +module_platform_driver(isp4_capture_drv); + +MODULE_ALIAS("platform:" ISP4_DRV_NAME); +MODULE_IMPORT_NS("DMA_BUF"); + +MODULE_DESCRIPTION("AMD ISP4 Driver"); +MODULE_AUTHOR("Bin Du "); +MODULE_AUTHOR("Pratap Nirujogi "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h new file mode 100644 index 00000000000000..7f2db0dfa2d9f5 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_H_ +#define _ISP4_H_ + +#include +#include + +struct isp4_device { + struct v4l2_device v4l2_dev; + struct media_device mdev; +}; + +#endif /* _ISP4_H_ */ From 0857111dfbafd518d45b3604640256adc5070730 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Fri, 20 Mar 2026 16:41:41 +0800 Subject: [PATCH 164/521] FROMLIST: media: platform: amd: low level support for isp4 firmware Low level functions for accessing the registers and mapping to their ranges. This change also includes register definitions for ring buffer used to communicate with ISP Firmware. Ring buffer is the communication interface between driver and ISP Firmware. Command and responses are exchanged through the ring buffer. Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Link: https://lore.kernel.org/all/20260320084146.200988-1-Bin.Du@amd.com/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 1 + drivers/media/platform/amd/isp4/isp4_hw_reg.h | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 drivers/media/platform/amd/isp4/isp4_hw_reg.h diff --git a/MAINTAINERS b/MAINTAINERS index afc2698456a414..4ec135d5118bda 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1171,6 +1171,7 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_hw_reg.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/isp4_hw_reg.h b/drivers/media/platform/amd/isp4/isp4_hw_reg.h new file mode 100644 index 00000000000000..09c76f75c5ee92 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_hw_reg.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_HW_REG_H_ +#define _ISP4_HW_REG_H_ + +#include + +#define ISP_SOFT_RESET 0x62000 +#define ISP_SYS_INT0_EN 0x62010 +#define ISP_SYS_INT0_STATUS 0x62014 +#define ISP_SYS_INT0_ACK 0x62018 +#define ISP_CCPU_CNTL 0x62054 +#define ISP_STATUS 0x62058 +#define ISP_LOG_RB_BASE_LO0 0x62148 +#define ISP_LOG_RB_BASE_HI0 0x6214c +#define ISP_LOG_RB_SIZE0 0x62150 +#define ISP_LOG_RB_RPTR0 0x62154 +#define ISP_LOG_RB_WPTR0 0x62158 +#define ISP_RB_BASE_LO1 0x62170 +#define ISP_RB_BASE_HI1 0x62174 +#define ISP_RB_SIZE1 0x62178 +#define ISP_RB_RPTR1 0x6217c +#define ISP_RB_WPTR1 0x62180 +#define ISP_RB_BASE_LO2 0x62184 +#define ISP_RB_BASE_HI2 0x62188 +#define ISP_RB_SIZE2 0x6218c +#define ISP_RB_RPTR2 0x62190 +#define ISP_RB_WPTR2 0x62194 +#define ISP_RB_BASE_LO3 0x62198 +#define ISP_RB_BASE_HI3 0x6219c +#define ISP_RB_SIZE3 0x621a0 +#define ISP_RB_RPTR3 0x621a4 +#define ISP_RB_WPTR3 0x621a8 +#define ISP_RB_BASE_LO4 0x621ac +#define ISP_RB_BASE_HI4 0x621b0 +#define ISP_RB_SIZE4 0x621b4 +#define ISP_RB_RPTR4 0x621b8 +#define ISP_RB_WPTR4 0x621bc +#define ISP_RB_BASE_LO5 0x621c0 +#define ISP_RB_BASE_HI5 0x621c4 +#define ISP_RB_SIZE5 0x621c8 +#define ISP_RB_RPTR5 0x621cc +#define ISP_RB_WPTR5 0x621d0 +#define ISP_RB_BASE_LO6 0x621d4 +#define ISP_RB_BASE_HI6 0x621d8 +#define ISP_RB_SIZE6 0x621dc +#define ISP_RB_RPTR6 0x621e0 +#define ISP_RB_WPTR6 0x621e4 +#define ISP_RB_BASE_LO7 0x621e8 +#define ISP_RB_BASE_HI7 0x621ec +#define ISP_RB_SIZE7 0x621f0 +#define ISP_RB_RPTR7 0x621f4 +#define ISP_RB_WPTR7 0x621f8 +#define ISP_RB_BASE_LO8 0x621fc +#define ISP_RB_BASE_HI8 0x62200 +#define ISP_RB_SIZE8 0x62204 +#define ISP_RB_RPTR8 0x62208 +#define ISP_RB_WPTR8 0x6220c +#define ISP_RB_BASE_LO9 0x62210 +#define ISP_RB_BASE_HI9 0x62214 +#define ISP_RB_SIZE9 0x62218 +#define ISP_RB_RPTR9 0x6221c +#define ISP_RB_WPTR9 0x62220 +#define ISP_RB_BASE_LO10 0x62224 +#define ISP_RB_BASE_HI10 0x62228 +#define ISP_RB_SIZE10 0x6222c +#define ISP_RB_RPTR10 0x62230 +#define ISP_RB_WPTR10 0x62234 +#define ISP_RB_BASE_LO11 0x62238 +#define ISP_RB_BASE_HI11 0x6223c +#define ISP_RB_SIZE11 0x62240 +#define ISP_RB_RPTR11 0x62244 +#define ISP_RB_WPTR11 0x62248 +#define ISP_RB_BASE_LO12 0x6224c +#define ISP_RB_BASE_HI12 0x62250 +#define ISP_RB_SIZE12 0x62254 +#define ISP_RB_RPTR12 0x62258 +#define ISP_RB_WPTR12 0x6225c + +#define ISP_POWER_STATUS 0x60000 + +/* ISP_SOFT_RESET */ +#define ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK 0x00000001UL + +/* ISP_CCPU_CNTL */ +#define ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK 0x00040000UL + +/* ISP_STATUS */ +#define ISP_STATUS__CCPU_REPORT_MASK 0x000000feUL + +/* ISP_SYS_INT0_STATUS */ +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK 0x00010000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK 0x00040000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK 0x00100000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK 0x00400000UL + +/* ISP_SYS_INT0_EN */ +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK 0x00010000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT10_EN_MASK 0x00040000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT11_EN_MASK 0x00100000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK 0x00400000UL + +/* ISP_SYS_INT0_ACK */ +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK 0x00010000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT10_ACK_MASK 0x00040000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT11_ACK_MASK 0x00100000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK 0x00400000UL + +/* Helper functions for reading isp registers */ +static inline u32 isp4hw_rreg(void __iomem *base, u32 reg) +{ + return readl(base + reg); +} + +/* Helper functions for writing isp registers */ +static inline void isp4hw_wreg(void __iomem *base, u32 reg, u32 val) +{ + return writel(val, base + reg); +} + +#endif /* _ISP4_HW_REG_H_ */ From fa05935fa546a7c819e470a45f95683f3d700579 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Fri, 20 Mar 2026 16:41:42 +0800 Subject: [PATCH 165/521] FROMLIST: media: platform: amd: Add isp4 fw and hw interface ISP firmware controls ISP HW pipeline using dedicated embedded processor called ccpu. The communication between ISP FW and driver is using commands and response messages sent through the ring buffer. Command buffers support either global setting that is not specific to the stream and support stream specific parameters. Response buffers contain ISP FW notification information such as frame buffer done and command done. IRQ is used for receiving response buffer from ISP firmware, which is handled in the main isp4 media device. ISP ccpu is booted up through the firmware loading helper function prior to stream start. Memory used for command buffer and response buffer needs to be allocated from amdgpu buffer manager because isp4 is a child device of amdgpu. Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Link: https://lore.kernel.org/all/20260320084146.200988-1-Bin.Du@amd.com/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 3 + drivers/media/platform/amd/isp4/Makefile | 3 +- .../platform/amd/isp4/isp4_fw_cmd_resp.h | 318 +++++++ .../media/platform/amd/isp4/isp4_interface.c | 827 ++++++++++++++++++ .../media/platform/amd/isp4/isp4_interface.h | 144 +++ 5 files changed, 1294 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.c create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.h diff --git a/MAINTAINERS b/MAINTAINERS index 4ec135d5118bda..2eafe5f49c9a59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1171,7 +1171,10 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h +F: drivers/media/platform/amd/isp4/isp4_interface.c +F: drivers/media/platform/amd/isp4/isp4_interface.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 500b81ce5d1473..c7eadd33fc978f 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -3,4 +3,5 @@ # Copyright (C) 2025 Advanced Micro Devices, Inc. obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o -amd_isp4_capture-objs := isp4.o +amd_isp4_capture-objs := isp4.o \ + isp4_interface.o diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h new file mode 100644 index 00000000000000..88bacb00355c49 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_FW_CMD_RESP_H_ +#define _ISP4_FW_CMD_RESP_H_ + +/* + * Two types of command/response channel. + * Type Global Command has one command/response channel. + * Type Stream Command has one command/response channel. + *----------- ------------ + *| | --------------------------- | | + *| | ---->| Global Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | --------------------------- | | + *| | ---->| Stream Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | | | + *| HOST | | Firmware | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Global Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Stream Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *----------- ------------ + */ + +/* + * cmd_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_CMD_TYPE_SHIFT 24 +#define ISP4FW_CMD_GROUP_SHIFT 16 +#define ISP4FW_CMD_TYPE_STREAM_CTRL (0x2U << ISP4FW_CMD_TYPE_SHIFT) + +#define ISP4FW_CMD_GROUP_STREAM_CTRL (0x1U << ISP4FW_CMD_GROUP_SHIFT) +#define ISP4FW_CMD_GROUP_STREAM_BUFFER (0x4U << ISP4FW_CMD_GROUP_SHIFT) + +/* Stream Command */ +#define ISP4FW_CMD_ID_SET_STREAM_CONFIG (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x1) +#define ISP4FW_CMD_ID_SET_OUT_CHAN_PROP (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x3) +#define ISP4FW_CMD_ID_ENABLE_OUT_CHAN (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x5) +#define ISP4FW_CMD_ID_START_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x7) +#define ISP4FW_CMD_ID_STOP_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x8) + +/* Stream Buffer Command */ +#define ISP4FW_CMD_ID_SEND_BUFFER (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_BUFFER | 0x1) + +/* + * resp_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_RESP_GROUP_SHIFT 16 + +#define ISP4FW_RESP_GROUP_GENERAL (0x1 << ISP4FW_RESP_GROUP_SHIFT) +#define ISP4FW_RESP_GROUP_NOTIFICATION (0x3 << ISP4FW_RESP_GROUP_SHIFT) + +/* General Response */ +#define ISP4FW_RESP_ID_CMD_DONE (ISP4FW_RESP_GROUP_GENERAL | 0x1) + +/* Notification */ +#define ISP4FW_RESP_ID_NOTI_FRAME_DONE (ISP4FW_RESP_GROUP_NOTIFICATION | 0x1) + +#define ISP4FW_CMD_STATUS_SUCCESS 0 +#define ISP4FW_CMD_STATUS_FAIL 1 +#define ISP4FW_CMD_STATUS_SKIPPED 2 + +#define ISP4FW_ADDR_SPACE_TYPE_GPU_VA 4 + +#define ISP4FW_MEMORY_POOL_SIZE (100 * 1024 * 1024) + +/* + * standard ISP pipeline: mipicsi=>isp + */ +#define ISP4FW_MIPI0_ISP_PIPELINE_ID 0x5f91 + +enum isp4fw_sensor_id { + /* Sensor id for ISP input from MIPI port 0 */ + ISP4FW_SENSOR_ID_ON_MIPI0 = 0, +}; + +enum isp4fw_stream_id { + ISP4FW_STREAM_ID_INVALID = -1, + ISP4FW_STREAM_ID_1 = 0, + ISP4FW_STREAM_ID_2 = 1, + ISP4FW_STREAM_ID_3 = 2, + ISP4FW_STREAM_ID_MAXIMUM +}; + +enum isp4fw_image_format { + /* 4:2:0,semi-planar, 8-bit */ + ISP4FW_IMAGE_FORMAT_NV12 = 1, + /* interleave, 4:2:2, 8-bit */ + ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED = 7, +}; + +enum isp4fw_pipe_out_ch { + ISP4FW_ISP_PIPE_OUT_CH_PREVIEW = 0, +}; + +enum isp4fw_yuv_range { + ISP4FW_ISP_YUV_RANGE_FULL = 0, /* YUV value range in 0~255 */ + ISP4FW_ISP_YUV_RANGE_NARROW = 1, /* YUV value range in 16~235 */ + ISP4FW_ISP_YUV_RANGE_MAX +}; + +enum isp4fw_buffer_type { + ISP4FW_BUFFER_TYPE_PREVIEW = 8, + ISP4FW_BUFFER_TYPE_META_INFO = 10, + ISP4FW_BUFFER_TYPE_MEM_POOL = 15, +}; + +enum isp4fw_buffer_status { + /* The buffer is INVALID */ + ISP4FW_BUFFER_STATUS_INVALID, + /* The buffer is not filled with image data */ + ISP4FW_BUFFER_STATUS_SKIPPED, + /* The buffer is available and awaiting to be filled */ + ISP4FW_BUFFER_STATUS_EXIST, + /* The buffer is filled with image data */ + ISP4FW_BUFFER_STATUS_DONE, + /* The buffer is unavailable */ + ISP4FW_BUFFER_STATUS_LACK, + /* The buffer is dirty, probably caused by LMI leakage */ + ISP4FW_BUFFER_STATUS_DIRTY, + ISP4FW_BUFFER_STATUS_MAX +}; + +enum isp4fw_buffer_source { + /* The buffer is from the stream buffer queue */ + ISP4FW_BUFFER_SOURCE_STREAM, +}; + +struct isp4fw_error_code { + u32 code1; + u32 code2; + u32 code3; + u32 code4; + u32 code5; +}; + +/* Command Structure for FW */ + +struct isp4fw_cmd { + u32 cmd_seq_num; + u32 cmd_id; + u32 cmd_param[12]; + u16 cmd_stream_id; + u8 cmd_silent_resp; + u8 reserved; + u32 cmd_check_sum; +}; + +struct isp4fw_resp_cmd_done { + /* + * The host2fw command seqNum. + * To indicate which command this response refers to. + */ + u32 cmd_seq_num; + /* The host2fw command id for host double check. */ + u32 cmd_id; + /* + * Indicate the command process status. + * 0 means success. 1 means fail. 2 means skipped + */ + u16 cmd_status; + /* + * If cmd_status is 1, the command failed. The host can check + * isp4fw_error_code for details. + */ + u16 isp4fw_error_code; + /* The response payload type varies by cmd. */ + u8 payload[36]; +}; + +struct isp4fw_resp_param_package { + u32 package_addr_lo; /* The low 32 bit of the pkg address. */ + u32 package_addr_hi; /* The high 32 bit of the pkg address. */ + u32 package_size; /* The total pkg size in bytes. */ + u32 package_check_sum; /* The byte sum of the pkg. */ +}; + +struct isp4fw_resp { + u32 resp_seq_num; + u32 resp_id; + union { + struct isp4fw_resp_cmd_done cmd_done; + struct isp4fw_resp_param_package frame_done; + u32 resp_param[12]; + } param; + u8 reserved[4]; + u32 resp_check_sum; +}; + +struct isp4fw_mipi_pipe_path_cfg { + u32 b_enable; + enum isp4fw_sensor_id isp4fw_sensor_id; +}; + +struct isp4fw_isp_pipe_path_cfg { + u32 isp_pipe_id; /* pipe ids for pipeline construction */ +}; + +struct isp4fw_isp_stream_cfg { + /* Isp mipi path */ + struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg; + /* Isp pipe path */ + struct isp4fw_isp_pipe_path_cfg isp_pipe_path_cfg; + /* enable TNR */ + u32 b_enable_tnr; + /* + * Number of frames for RTA processing. + * Set to 0 to use the firmware's default value. + */ + u32 rta_frames_per_proc; +}; + +struct isp4fw_image_prop { + enum isp4fw_image_format image_format; + u32 width; + u32 height; + u32 luma_pitch; + u32 chroma_pitch; + enum isp4fw_yuv_range yuv_range; +}; + +struct isp4fw_buffer { + /* + * A check num for debug usage, host can set the buf_tags + * to different numbers + */ + u32 buf_tags; + union { + u32 value; + struct { + u32 space : 16; + u32 vmid : 16; + } bit; + } vmid_space; + u32 buf_base_a_lo; /* Low address of buffer A */ + u32 buf_base_a_hi; /* High address of buffer A */ + u32 buf_size_a; /* Buffer size of buffer A */ + + u32 buf_base_b_lo; /* Low address of buffer B */ + u32 buf_base_b_hi; /* High address of buffer B */ + u32 buf_size_b; /* Buffer size of buffer B */ + + u32 buf_base_c_lo; /* Low address of buffer C */ + u32 buf_base_c_hi; /* High address of buffer C */ + u32 buf_size_c; /* Buffer size of buffer C */ +}; + +struct isp4fw_buffer_meta_info { + u32 enabled; /* enabled flag */ + enum isp4fw_buffer_status status; /* BufferStatus */ + struct isp4fw_error_code err; /* err code */ + enum isp4fw_buffer_source source; /* BufferSource */ + struct isp4fw_image_prop image_prop; /* image_prop */ + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_meta_info { + u32 poc; /* frame id */ + u32 fc_id; /* frame ctl id */ + u32 time_stamp_lo; /* timestamp low 32 bits */ + u32 time_stamp_hi; /* timestamp_high 32 bits */ + struct isp4fw_buffer_meta_info preview; /* preview BufferMetaInfo */ +}; + +struct isp4fw_cmd_send_buffer { + enum isp4fw_buffer_type buffer_type; + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_cmd_set_out_ch_prop { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + struct isp4fw_image_prop image_prop; /* image property */ +}; + +struct isp4fw_cmd_enable_out_ch { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + u32 is_enable; /* If channel is enabled or not */ +}; + +struct isp4fw_cmd_set_stream_cfg { + struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */ +}; + +#endif /* _ISP4_FW_CMD_RESP_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c new file mode 100644 index 00000000000000..c35ff5c7273e79 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -0,0 +1,827 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \ + (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\ + | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK) + +#define ISP4IF_FW_CMD_TIMEOUT (HZ / 2) + +struct isp4if_rb_config { + const char *name; + u32 index; + u32 reg_rptr; + u32 reg_wptr; + u32 reg_base_lo; + u32 reg_base_hi; + u32 reg_size; + u32 val_size; + u64 base_mc_addr; + void *base_sys_addr; +}; + +/* FW cmd ring buffer configuration */ +static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "CMD_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR4, + .reg_wptr = ISP_RB_WPTR4, + .reg_base_lo = ISP_RB_BASE_LO4, + .reg_base_hi = ISP_RB_BASE_HI4, + .reg_size = ISP_RB_SIZE4, + }, + { + .name = "CMD_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR1, + .reg_wptr = ISP_RB_WPTR1, + .reg_base_lo = ISP_RB_BASE_LO1, + .reg_base_hi = ISP_RB_BASE_HI1, + .reg_size = ISP_RB_SIZE1, + }, + { + .name = "CMD_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR2, + .reg_wptr = ISP_RB_WPTR2, + .reg_base_lo = ISP_RB_BASE_LO2, + .reg_base_hi = ISP_RB_BASE_HI2, + .reg_size = ISP_RB_SIZE2, + }, + { + .name = "CMD_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR3, + .reg_wptr = ISP_RB_WPTR3, + .reg_base_lo = ISP_RB_BASE_LO3, + .reg_base_hi = ISP_RB_BASE_HI3, + .reg_size = ISP_RB_SIZE3, + }, +}; + +/* FW resp ring buffer configuration */ +static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "RES_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR12, + .reg_wptr = ISP_RB_WPTR12, + .reg_base_lo = ISP_RB_BASE_LO12, + .reg_base_hi = ISP_RB_BASE_HI12, + .reg_size = ISP_RB_SIZE12, + }, + { + .name = "RES_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR9, + .reg_wptr = ISP_RB_WPTR9, + .reg_base_lo = ISP_RB_BASE_LO9, + .reg_base_hi = ISP_RB_BASE_HI9, + .reg_size = ISP_RB_SIZE9, + }, + { + .name = "RES_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR10, + .reg_wptr = ISP_RB_WPTR10, + .reg_base_lo = ISP_RB_BASE_LO10, + .reg_base_hi = ISP_RB_BASE_HI10, + .reg_size = ISP_RB_SIZE10, + }, + { + .name = "RES_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR11, + .reg_wptr = ISP_RB_WPTR11, + .reg_base_lo = ISP_RB_BASE_LO11, + .reg_base_hi = ISP_RB_BASE_HI11, + .reg_size = ISP_RB_SIZE11, + }, +}; + +/* FW log ring buffer configuration */ +static struct isp4if_rb_config isp4if_log_rb_config = { + .name = "LOG_RB", + .index = 0, + .reg_rptr = ISP_LOG_RB_RPTR0, + .reg_wptr = ISP_LOG_RB_WPTR0, + .reg_base_lo = ISP_LOG_RB_BASE_LO0, + .reg_base_hi = ISP_LOG_RB_BASE_HI0, + .reg_size = ISP_LOG_RB_SIZE0, +}; + +static struct isp4if_gpu_mem_info * +isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size) +{ + struct isp4if_gpu_mem_info *mem_info; + struct device *dev = ispif->dev; + int ret; + + mem_info = kmalloc(sizeof(*mem_info), GFP_KERNEL); + if (!mem_info) + return NULL; + + mem_info->mem_size = mem_size; + ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size, + &mem_info->mem_handle, + &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + if (ret) { + kfree(mem_info); + return NULL; + } + + return mem_info; +} + +static void isp4if_gpu_mem_free(struct isp4_interface *ispif, + struct isp4if_gpu_mem_info **mem_info_ptr) +{ + struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr; + struct device *dev = ispif->dev; + + if (!mem_info) { + dev_err(dev, "invalid mem_info\n"); + return; + } + + *mem_info_ptr = NULL; + isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + kfree(mem_info); +} + +static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif) +{ + isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool); + isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf); + isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf); + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) + isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]); +} + +static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif, + ISP4FW_MEMORY_POOL_SIZE); + if (!ispif->fw_mem_pool) + goto error_no_memory; + + ispif->fw_cmd_resp_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE); + if (!ispif->fw_cmd_resp_buf) + goto error_no_memory; + + ispif->fw_log_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE); + if (!ispif->fw_log_buf) + goto error_no_memory; + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + ispif->meta_info_buf[i] = + isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE); + if (!ispif->meta_info_buf[i]) + goto error_no_memory; + } + + return 0; + +error_no_memory: + dev_err(dev, "failed to allocate gpu memory\n"); + return -ENOMEM; +} + +static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size) +{ + const u8 *surplus_ptr; + const u32 *buffer; + u32 checksum = 0; + size_t i; + + buffer = (const u32 *)buf; + for (i = 0; i < buf_size / sizeof(u32); i++) + checksum += buffer[i]; + + surplus_ptr = (const u8 *)&buffer[i]; + /* add surplus data crc checksum */ + for (i = 0; i < buf_size % sizeof(u32); i++) + checksum += surplus_ptr[i]; + + return checksum; +} + +void isp4if_clear_cmdq(struct isp4_interface *ispif) +{ + struct isp4if_cmd_element *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->cmdq_lock) + list_splice_init(&ispif->cmdq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, list) + kfree(buf_node); +} + +static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif, + enum isp4if_stream_id stream) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_free; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + + /* + * Read and write pointers are equal, indicating the ring buffer + * is empty + */ + if (wr_ptr == rd_ptr) + return false; + + if (wr_ptr > rd_ptr) + bytes_free = len - (wr_ptr - rd_ptr); + else + bytes_free = rd_ptr - wr_ptr; + + /* + * Ignore one byte from the bytes free to prevent rd_ptr from equaling + * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is + * supposed to indicate that the ring buffer is empty. + */ + return bytes_free <= sizeof(struct isp4fw_cmd); +} + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id) +{ + struct isp4if_cmd_element *ele; + + guard(spinlock)(&ispif->cmdq_lock); + + list_for_each_entry(ele, &ispif->cmdq, list) { + if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) { + list_del(&ele->list); + return ele; + } + } + + return NULL; +} + +/* Must check that isp4if_is_cmdq_rb_full() == false before calling */ +static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, + enum isp4if_stream_id stream, + const struct isp4fw_cmd *cmd) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 cmd_sz = sizeof(*cmd); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + const void *src = cmd; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) { + dev_err(dev, + "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, cmd_sz=%u\n", + stream, rd_ptr, wr_ptr, len, cmd_sz); + return -EINVAL; + } + + bytes_to_end = len - wr_ptr; + if (bytes_to_end >= cmd_sz) { + /* FW cmd is just a straight copy to the write pointer */ + memcpy(mem_sys + wr_ptr, src, cmd_sz); + isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len); + } else { + /* + * FW cmd is split because the ring buffer needs to wrap + * around + */ + memcpy(mem_sys + wr_ptr, src, bytes_to_end); + memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end); + } + + return 0; +} + +static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id) +{ + return ISP4IF_STREAM_ID_1; +} + +static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, + const void *package, + u32 package_size, bool sync) +{ + enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id); + struct isp4if_cmd_element *ele = NULL; + struct device *dev = ispif->dev; + struct isp4fw_cmd cmd; + u32 seq_num; + int ret; + + if (package_size > sizeof(cmd.cmd_param)) { + dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n", + package_size, sizeof(cmd.cmd_param), cmd_id, stream); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = cmd_id; + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID; + break; + case ISP4IF_STREAM_ID_1: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_1; + break; + default: + dev_err(dev, "fail bad stream id %d\n", stream); + return -EINVAL; + } + + /* Allocate the sync command object early and outside of the lock */ + if (sync) { + ele = kmalloc(sizeof(*ele), GFP_KERNEL); + if (!ele) + return -ENOMEM; + + /* Get two references: one for the resp thread, one for us */ + atomic_set(&ele->refcnt, 2); + init_completion(&ele->cmd_done); + } + + if (package && package_size) + memcpy(cmd.cmd_param, package, package_size); + + scoped_guard(mutex, &ispif->isp4if_mutex) { + ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret, + ISP4IF_RB_FULL_SLEEP_US, + ISP4IF_RB_FULL_TIMEOUT_US, false, ispif, + stream); + if (ret) { + struct isp4if_rb_config *rb_config = + &isp4if_resp_rb_config[stream]; + u32 rd_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_rptr); + u32 wr_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_wptr); + + dev_err(dev, + "fail to get free cmdq slot, stream (%d),rd %u, wr %u\n", + stream, rd_ptr, wr_ptr); + ret = -ETIMEDOUT; + goto free_ele; + } + + seq_num = ispif->host2fw_seq_num++; + cmd.cmd_seq_num = seq_num; + cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd) + - sizeof(u32)); + + /* + * only append the fw cmd to queue when its response needs to + * be waited for, currently there are only two such commands, + * disable channel and stop stream which are only sent after + * close camera + */ + if (ele) { + ele->seq_num = seq_num; + ele->cmd_id = cmd_id; + scoped_guard(spinlock, &ispif->cmdq_lock) + list_add_tail(&ele->list, &ispif->cmdq); + } + + ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); + if (ret) { + dev_err(dev, + "fail for insert_isp_fw_cmd cmd_id (0x%08x)\n", + cmd_id); + goto err_dequeue_ele; + } + } + + if (ele) { + ret = wait_for_completion_timeout(&ele->cmd_done, + ISP4IF_FW_CMD_TIMEOUT); + if (!ret) { + ret = -ETIMEDOUT; + goto err_dequeue_ele; + } + + ret = 0; + goto put_ele_ref; + } + + return 0; + +err_dequeue_ele: + /* + * Try to remove the command from the queue. If that fails, then it + * means the response thread is currently using the object, and we need + * to use the refcount to avoid a use-after-free by either side. + */ + if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id)) + goto free_ele; + +put_ele_ref: + /* Don't free the command if we didn't put the last reference */ + if (ele && atomic_dec_return(&ele->refcnt)) + ele = NULL; + +free_ele: + kfree(ele); + return ret; +} + +static int isp4if_send_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4fw_cmd_send_buffer cmd; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW; + cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(buf_info->planes[0].mc_addr, + &cmd.buffer.buf_base_a_lo, + &cmd.buffer.buf_base_a_hi); + cmd.buffer.buf_size_a = buf_info->planes[0].len; + + isp4if_split_addr64(buf_info->planes[1].mc_addr, + &cmd.buffer.buf_base_b_lo, + &cmd.buffer.buf_base_b_hi); + cmd.buffer.buf_size_b = buf_info->planes[1].len; + + isp4if_split_addr64(buf_info->planes[2].mc_addr, + &cmd.buffer.buf_base_c_lo, + &cmd.buffer.buf_base_c_hi); + cmd.buffer.buf_size_c = buf_info->planes[2].len; + + return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd, + sizeof(cmd), false); +} + +static void isp4if_init_rb_config(struct isp4_interface *ispif, + struct isp4if_rb_config *rb_config) +{ + isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo, + rb_config->base_mc_addr); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi, + rb_config->base_mc_addr >> 32); + isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size); +} + +static int isp4if_fw_init(struct isp4_interface *ispif) +{ + u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0; + struct isp4if_rb_config *rb_config; + u32 offset; + unsigned int i; + + /* initialize CMD_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_cmd_rb_config + i); + offset = aligned_rb_chunk_size * rb_config->index; + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize RESP_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_resp_rb_config + i); + offset = aligned_rb_chunk_size * + (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1); + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize LOG_RB stream */ + rb_config = &isp4if_log_rb_config; + rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE; + rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr; + rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr; + + isp4if_init_rb_config(ispif, rb_config); + + return 0; +} + +static int isp4if_wait_fw_ready(struct isp4_interface *ispif, + u32 isp_status_addr) +{ + struct device *dev = ispif->dev; + u32 timeout_ms = 100; + u32 interval_ms = 1; + u32 reg_val; + + /* wait for FW initialize done! */ + if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val + & ISP_STATUS__CCPU_REPORT_MASK, + interval_ms * 1000, timeout_ms * 1000, false, + ispif->mmio, isp_status_addr)) + return 0; + + dev_err(dev, "ISP CCPU FW boot failed\n"); + + return -ETIME; +} + +static void isp4if_enable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK); + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK); + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); +} + +static void isp4if_disable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK; + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK; + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); +} + +static int isp4if_fw_boot(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + if (ispif->status != ISP4IF_STATUS_PWR_ON) { + dev_err(dev, "invalid isp power status %d\n", ispif->status); + return -EINVAL; + } + + isp4if_disable_ccpu(ispif); + + isp4if_fw_init(ispif); + + /* clear ccpu status */ + isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0); + + isp4if_enable_ccpu(ispif); + + if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) { + isp4if_disable_ccpu(ispif); + return -EINVAL; + } + + /* enable interrupts */ + isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN, + ISP4IF_FW_RESP_RB_IRQ_EN_MASK); + + ispif->status = ISP4IF_STATUS_FW_RUNNING; + + dev_dbg(dev, "ISP CCPU FW boot success\n"); + + return 0; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp) +{ + struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 resp_sz = sizeof(*resp); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + void *dst = resp; + u32 checksum; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) + goto err_rb_invalid; + + /* + * Read and write pointers are equal, indicating the ring buffer is + * empty + */ + if (rd_ptr == wr_ptr) + return -ENODATA; + + bytes_to_end = len - rd_ptr; + if (bytes_to_end >= resp_sz) { + /* FW response is just a straight copy from the read pointer */ + if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, resp_sz); + isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len); + } else { + /* + * FW response is split because the ring buffer wrapped + * around + */ + if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, bytes_to_end); + memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end); + } + + checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32)); + if (checksum != resp->resp_check_sum) { + dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", + checksum, resp->resp_check_sum, rd_ptr, wr_ptr); + dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", + stream, resp->resp_seq_num, + resp->resp_id); + return -EINVAL; + } + + return 0; + +err_rb_invalid: + dev_err(dev, + "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, resp_sz=%u\n", + stream, rd_ptr, wr_ptr, len, resp_sz); + return -EINVAL; +} + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false); +} + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true); +} + +void isp4if_clear_bufq(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->bufq_lock) + list_splice_init(&ispif->bufq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, node) + kfree(buf_node); +} + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node) +{ + kfree(buf_node); +} + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info) +{ + struct isp4if_img_buf_node *node; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (node) + node->buf_info = *buf_info; + + return node; +} + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node; + + guard(spinlock)(&ispif->bufq_lock); + + buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node), + node); + if (buf_node) + list_del(&buf_node->node); + + return buf_node; +} + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node) +{ + int ret; + + ret = isp4if_send_buffer(ispif, &buf_node->buf_info); + if (ret) + return ret; + + scoped_guard(spinlock, &ispif->bufq_lock) + list_add_tail(&buf_node->node, &ispif->bufq); + + return 0; +} + +int isp4if_stop(struct isp4_interface *ispif) +{ + isp4if_disable_ccpu(ispif); + + isp4if_dealloc_fw_gpumem(ispif); + + return 0; +} + +int isp4if_start(struct isp4_interface *ispif) +{ + int ret; + + ret = isp4if_alloc_fw_gpumem(ispif); + if (ret) + return ret; + + ret = isp4if_fw_boot(ispif); + if (ret) + goto failed_fw_boot; + + return 0; + +failed_fw_boot: + isp4if_dealloc_fw_gpumem(ispif); + return ret; +} + +int isp4if_deinit(struct isp4_interface *ispif) +{ + isp4if_clear_cmdq(ispif); + + isp4if_clear_bufq(ispif); + + mutex_destroy(&ispif->isp4if_mutex); + + return 0; +} + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio) +{ + ispif->dev = dev; + ispif->mmio = isp_mmio; + + spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */ + spin_lock_init(&ispif->bufq_lock); /* used for bufq access */ + mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */ + + INIT_LIST_HEAD(&ispif->cmdq); + INIT_LIST_HEAD(&ispif->bufq); + + return 0; +} diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h new file mode 100644 index 00000000000000..ce3ac9b9e5cda1 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_INTERFACE_H_ +#define _ISP4_INTERFACE_H_ + +#include +#include +#include +#include + +struct isp4fw_resp; + +#define ISP4IF_RB_MAX 25 +#define ISP4IF_RESP_CHAN_TO_RB_OFFSET 9 +#define ISP4IF_RB_PMBMAP_MEM_SIZE (SZ_16M - 1) +#define ISP4IF_RB_PMBMAP_MEM_CHUNK \ + (ISP4IF_RB_PMBMAP_MEM_SIZE / (ISP4IF_RB_MAX - 1)) +#define ISP4IF_HOST2FW_COMMAND_SIZE sizeof(struct isp4fw_cmd) +#define ISP4IF_MAX_NUM_HOST2FW_COMMAND 40 +#define ISP4IF_FW_CMD_BUF_SIZE \ + (ISP4IF_MAX_NUM_HOST2FW_COMMAND * ISP4IF_HOST2FW_COMMAND_SIZE) +#define ISP4IF_RB_FULL_SLEEP_US (33 * USEC_PER_MSEC) +#define ISP4IF_RB_FULL_TIMEOUT_US (10 * ISP4IF_RB_FULL_SLEEP_US) + +#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) +#define ISP4IF_MAX_STREAM_BUF_COUNT 8 + +#define ISP4IF_FW_LOG_RINGBUF_SIZE SZ_2M + +enum isp4if_stream_id { + ISP4IF_STREAM_ID_GLOBAL = 0, + ISP4IF_STREAM_ID_1 = 1, + ISP4IF_STREAM_ID_MAX = 4 +}; + +enum isp4if_status { + ISP4IF_STATUS_PWR_OFF, + ISP4IF_STATUS_PWR_ON, + ISP4IF_STATUS_FW_RUNNING, + ISP4IF_FSM_STATUS_MAX +}; + +struct isp4if_gpu_mem_info { + u64 mem_size; + u64 gpu_mc_addr; + void *sys_addr; + void *mem_handle; +}; + +struct isp4if_img_buf_info { + struct { + void *sys_addr; + u64 mc_addr; + u32 len; + } planes[3]; +}; + +struct isp4if_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +struct isp4if_cmd_element { + struct list_head list; + u32 seq_num; + u32 cmd_id; + struct completion cmd_done; + atomic_t refcnt; +}; + +struct isp4_interface { + struct device *dev; + void __iomem *mmio; + + spinlock_t cmdq_lock; /* used for cmdq access */ + spinlock_t bufq_lock; /* used for bufq access */ + struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */ + + struct list_head cmdq; /* commands sent to fw */ + struct list_head bufq; /* buffers sent to fw */ + + enum isp4if_status status; + u32 host2fw_seq_num; + + /* ISP fw buffers */ + struct isp4if_gpu_mem_info *fw_log_buf; + struct isp4if_gpu_mem_info *fw_cmd_resp_buf; + struct isp4if_gpu_mem_info *fw_mem_pool; + struct isp4if_gpu_mem_info *meta_info_buf[ISP4IF_MAX_STREAM_BUF_COUNT]; +}; + +static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi) +{ + if (lo) + *lo = addr & 0xffffffff; + + if (hi) + *hi = addr >> 32; +} + +static inline u64 isp4if_join_addr64(u32 lo, u32 hi) +{ + return (((u64)hi) << 32) | (u64)lo; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp); + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id); + +void isp4if_clear_cmdq(struct isp4_interface *ispif); + +void isp4if_clear_bufq(struct isp4_interface *ispif); + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node); + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info); + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif); + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node); + +int isp4if_stop(struct isp4_interface *ispif); + +int isp4if_start(struct isp4_interface *ispif); + +int isp4if_deinit(struct isp4_interface *ispif); + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio); + +#endif /* _ISP4_INTERFACE_H_ */ From 6fe8961d5228233897dda11edd27d128471bdcfb Mon Sep 17 00:00:00 2001 From: Bin Du Date: Fri, 20 Mar 2026 16:41:43 +0800 Subject: [PATCH 166/521] FROMLIST: media: platform: amd: isp4 subdev and firmware loading handling added Isp4 sub-device is implementing v4l2 sub-device interface. It has one capture video node, and supports only preview stream. It manages firmware states, stream configuration. Add interrupt handling and notification for isp firmware to isp-subdevice. Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Link: https://lore.kernel.org/all/20260320084146.200988-1-Bin.Du@amd.com/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 104 +- drivers/media/platform/amd/isp4/isp4.h | 7 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 1029 +++++++++++++++++ drivers/media/platform/amd/isp4/isp4_subdev.h | 120 ++ 6 files changed, 1259 insertions(+), 6 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.c create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.h diff --git a/MAINTAINERS b/MAINTAINERS index 2eafe5f49c9a59..c8982c39530fd4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1175,6 +1175,8 @@ F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h F: drivers/media/platform/amd/isp4/isp4_interface.c F: drivers/media/platform/amd/isp4/isp4_interface.h +F: drivers/media/platform/amd/isp4/isp4_subdev.c +F: drivers/media/platform/amd/isp4/isp4_subdev.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index c7eadd33fc978f..7e4854b6a4cd60 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ - isp4_interface.o + isp4_interface.o \ + isp4_subdev.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 58b21258b6d351..e62f5d652d8138 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -3,13 +3,18 @@ * Copyright (C) 2025 Advanced Micro Devices, Inc. */ +#include #include #include #include #include "isp4.h" +#include "isp4_hw_reg.h" #define ISP4_DRV_NAME "amd_isp_capture" +#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \ + (ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK | \ + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK) static const struct { const char *name; @@ -17,27 +22,103 @@ static const struct { u32 en_mask; u32 ack_mask; u32 rb_int_num; -} isp4_irq[] = { +} isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */ { .name = "isp_irq_global", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK, .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ }, { .name = "isp_irq_stream1", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK, .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ }, }; +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable) +{ + u32 intr_en; + + /* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */ + spin_lock_irq(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN); + if (enable) + intr_en |= isp4_irq[index].en_mask; + else + intr_en &= ~isp4_irq[index].en_mask; + + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock_irq(&isp_subdev->irq_lock); +} + +static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index) +{ + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[index]; + + thread_ctx->resp_ready = true; + wake_up_interruptible(&thread_ctx->waitq); +} + static irqreturn_t isp4_irq_handler(int irq, void *arg) { + struct isp4_subdev *isp_subdev = arg; + u32 intr_ack = 0, intr_en = 0, intr_status; + int seen = 0; + + /* Get the ISP_SYS interrupt status */ + intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS); + intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK; + + /* Find which ISP_SYS interrupts fired */ + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + if (intr_status & isp4_irq[i].status_mask) { + intr_ack |= isp4_irq[i].ack_mask; + intr_en |= isp4_irq[i].en_mask; + seen |= BIT(i); + } + } + + /* + * Disable the ISP_SYS interrupts that fired. Must be done before waking + * the response threads, since they re-enable interrupts when finished. + * The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt(). + */ + spin_lock(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en; + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock(&isp_subdev->irq_lock); + + /* + * Clear the ISP_SYS interrupts. This must be done after the interrupts + * are disabled, so that ISP FW won't flag any new interrupts on these + * streams, and thus we don't need to clear interrupts again before + * re-enabling them in the response thread. + */ + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack); + + /* + * The operation `(seen >> i) << i` is logically equivalent to + * `seen &= ~BIT(i)`, with fewer instructions after compilation. + */ + for (int i; (i = ffs(seen)); seen = (seen >> i) << i) + isp4_wake_up_resp_thread(isp_subdev, i - 1); + return IRQ_HANDLED; } static int isp4_capture_probe(struct platform_device *pdev) { + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; struct device *dev = &pdev->dev; - int irq[ARRAY_SIZE(isp4_irq)]; + struct isp4_subdev *isp_subdev; struct isp4_device *isp_dev; int ret; @@ -47,6 +128,12 @@ static int isp4_capture_probe(struct platform_device *pdev) dev->init_name = ISP4_DRV_NAME; + isp_subdev = &isp_dev->isp_subdev; + isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(isp_subdev->mmio)) + return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio), + "isp ioremap fail\n"); + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num); if (irq[i] < 0) @@ -55,7 +142,8 @@ static int isp4_capture_probe(struct platform_device *pdev) isp4_irq[i].rb_int_num); ret = devm_request_irq(dev, irq[i], isp4_irq_handler, - IRQF_NO_AUTOEN, isp4_irq[i].name, dev); + IRQF_NO_AUTOEN, isp4_irq[i].name, + isp_subdev); if (ret) return dev_err_probe(dev, ret, "fail to req irq %d\n", irq[i]); @@ -78,6 +166,13 @@ static int isp4_capture_probe(struct platform_device *pdev) pm_runtime_set_suspended(dev); pm_runtime_enable(dev); + spin_lock_init(&isp_subdev->irq_lock); + ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq); + if (ret) { + dev_err_probe(dev, ret, "fail init isp4 sub dev\n"); + goto err_pm_disable; + } + ret = media_device_register(&isp_dev->mdev); if (ret) { dev_err_probe(dev, ret, "fail to register media device\n"); @@ -89,6 +184,8 @@ static int isp4_capture_probe(struct platform_device *pdev) return 0; err_isp4_deinit: + isp4sd_deinit(&isp_dev->isp_subdev); +err_pm_disable: pm_runtime_disable(dev); v4l2_device_unregister(&isp_dev->v4l2_dev); err_clean_media: @@ -103,6 +200,7 @@ static void isp4_capture_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; media_device_unregister(&isp_dev->mdev); + isp4sd_deinit(&isp_dev->isp_subdev); pm_runtime_disable(dev); v4l2_device_unregister(&isp_dev->v4l2_dev); media_device_cleanup(&isp_dev->mdev); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h index 7f2db0dfa2d9f5..2db6683d6d8b5d 100644 --- a/drivers/media/platform/amd/isp4/isp4.h +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -6,12 +6,15 @@ #ifndef _ISP4_H_ #define _ISP4_H_ -#include -#include +#include +#include "isp4_subdev.h" struct isp4_device { struct v4l2_device v4l2_dev; + struct isp4_subdev isp_subdev; struct media_device mdev; }; +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable); + #endif /* _ISP4_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c new file mode 100644 index 00000000000000..6d571b7f8840df --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -0,0 +1,1029 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include + +#include "isp4.h" +#include "isp4_fw_cmd_resp.h" +#include "isp4_interface.h" + +#define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4 + +#define ISP4SD_PERFORMANCE_STATE_LOW 0 +#define ISP4SD_PERFORMANCE_STATE_HIGH 1 + +/* align 32KB */ +#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) + +#define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev) + +static const char *isp4sd_entity_name = "amd isp4"; + +static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { + "amd_isp4_thread_global", + "amd_isp4_thread_stream1", +}; + +static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable) +{ + if (isp_subdev->enable_gpio) { + gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0); + dev_dbg(isp_subdev->dev, "%s isp_subdev module\n", + enable ? "enable" : "disable"); + } +} + +static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + int ret; + + if (!ispif->fw_mem_pool) { + dev_err(dev, "fail to alloc mem pool\n"); + return -ENOMEM; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size; + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n", + ispif->fw_mem_pool->gpu_mc_addr, + buf_type.buffer.buf_size_a, ret); + return ret; + } + + dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n", + ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a); + + return 0; +} + +static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_stream_cfg cmd; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id = + ISP4FW_SENSOR_ID_ON_MIPI0; + cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true; + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id = + ISP4FW_MIPI0_ISP_PIPELINE_ID; + + cmd.stream_cfg.b_enable_tnr = true; + dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n", + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id, + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id, + cmd.stream_cfg.b_enable_tnr); + + return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG, + &cmd, sizeof(cmd)); +} + +static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + int ret; + + if (!meta_info_buf) { + dev_err(dev, "fail for no meta info buf(%u)\n", i); + return -ENOMEM; + } + + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = + ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_buf->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = meta_info_buf->mem_size; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send meta info(%u) fail\n", i); + return ret; + } + } + + dev_dbg(dev, "send meta info suc\n"); + return 0; +} + +static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev, + struct isp4fw_image_prop *out_prop, + struct v4l2_subdev_state *state, u32 pad) +{ + struct device *dev = isp_subdev->dev; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(dev, "fail get subdev state format\n"); + return false; + } + + switch (format->code) { + case MEDIA_BUS_FMT_YUYV8_1_5X8: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width; + out_prop->chroma_pitch = out_prop->width; + break; + case MEDIA_BUS_FMT_YUYV8_1X16: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width * 2; + out_prop->chroma_pitch = 0; + break; + default: + dev_err(dev, "fail for bad image format:0x%x\n", + format->code); + return false; + } + + if (!out_prop->width || !out_prop->height) + return false; + + return true; +} + +static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + dev_dbg(dev, "w:%u,h:%u\n", w, h); + + if (isp4sd_send_meta_buf(isp_subdev)) { + dev_err(dev, "fail to send meta buf\n"); + sensor_info->status = ISP4SD_START_STATUS_START_FAIL; + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (!sensor_info->start_stream_cmd_sent && + sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to start stream\n"); + return ret; + } + + sensor_info->start_stream_cmd_sent = true; + } else { + dev_dbg(dev, + "no send START_STREAM, start_sent %u, buf_sent %u\n", + sensor_info->start_stream_cmd_sent, + sensor_info->buf_sent_cnt); + } + + return 0; +} + +static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop; + struct isp4fw_cmd_enable_out_ch cmd_ch_en; + struct device *dev = isp_subdev->dev; + int ret; + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop)); + cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + + if (!isp4sd_get_str_out_prop(isp_subdev, + &cmd_ch_prop.image_prop, state, pad)) { + dev_err(dev, "fail to get out prop\n"); + return -EINVAL; + } + + dev_dbg(dev, "channel:%d,fmt %d,w:h=%u:%u,lp:%u,cp%u\n", + cmd_ch_prop.ch, + cmd_ch_prop.image_prop.image_format, + cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, + cmd_ch_prop.image_prop.luma_pitch, + cmd_ch_prop.image_prop.chroma_pitch); + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP, + &cmd_ch_prop, sizeof(cmd_ch_prop)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to set out prop\n"); + return ret; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_en, 0, sizeof(cmd_ch_en)); + cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + cmd_ch_en.is_enable = true; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_en, sizeof(cmd_ch_en)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to enable channel\n"); + return ret; + } + + dev_dbg(dev, "enable channel %d\n", cmd_ch_en.ch); + + if (!sensor_info->start_stream_cmd_sent) { + ret = isp4sd_kickoff_stream(isp_subdev, + cmd_ch_prop.image_prop.width, + cmd_ch_prop.image_prop.height); + if (ret) { + dev_err(dev, "kickoff stream fail %d\n", ret); + return ret; + } + /* + * sensor_info->start_stream_cmd_sent will be set to true + * 1. in isp4sd_kickoff_stream, if app first send buffer then + * start stream + * 2. in isp_set_stream_buf, if app first start stream, then + * send buffer because ISP FW has the requirement, host needs + * to send buffer before send start stream cmd + */ + if (sensor_info->start_stream_cmd_sent) { + sensor_info->status = ISP4SD_START_STATUS_STARTED; + output_info->start_status = ISP4SD_START_STATUS_STARTED; + dev_dbg(dev, "kickoff stream suc,start cmd sent\n"); + } + } else { + dev_dbg(dev, "stream running, no need kickoff\n"); + output_info->start_status = ISP4SD_START_STATUS_STARTED; + } + + dev_dbg(dev, "setup output suc\n"); + return 0; +} + +static int isp4sd_init_stream(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + int ret; + + ret = isp4sd_setup_fw_mem_pool(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup fw mem pool\n"); + return ret; + } + + ret = isp4sd_set_stream_path(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup stream path\n"); + return ret; + } + + return 0; +} + +static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(isp_subdev->dev, "fail to get v4l2 format\n"); + } else { + memset(format, 0, sizeof(*format)); + format->code = MEDIA_BUS_FMT_YUYV8_1_5X8; + } + + isp4if_clear_bufq(ispif); + isp4if_clear_cmdq(ispif); + + sensor_info->start_stream_cmd_sent = false; + sensor_info->buf_sent_cnt = 0; + + sensor_info->status = ISP4SD_START_STATUS_OFF; + output_info->start_status = ISP4SD_START_STATUS_OFF; +} + +static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_cmd_done *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_cmd_element *ele = + isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n", + stream_id, + para->cmd_id, para->cmd_status, para->cmd_seq_num, + ele); + + if (ele) { + complete(&ele->cmd_done); + if (atomic_dec_and_test(&ele->refcnt)) + kfree(ele); + } +} + +static struct isp4fw_meta_info * +isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc) +{ + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + + if (meta_info_buf->gpu_mc_addr == mc) + return meta_info_buf->sys_addr; + } + + return NULL; +} + +static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev, + u64 meta_info_mc) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) { + dev_warn(dev, "not working status %i, meta_info 0x%llx\n", + isp_subdev->sensor_info.status, meta_info_mc); + return; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_mc, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE; + + if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type))) + dev_err(dev, "fail send meta_info 0x%llx\n", + meta_info_mc); + else + dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc); +} + +static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_param_package *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4if_img_buf_node *prev; + struct isp4fw_meta_info *meta; + u64 mc; + + mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi); + meta = isp4sd_get_meta_by_mc(isp_subdev, mc); + if (!meta) { + dev_err(dev, "fail to get meta from mc %llx\n", mc); + return; + } + + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%i\n", + ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + meta->preview.status); + + if (meta->preview.enabled && + (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED || + meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || + meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { + prev = isp4if_dequeue_buffer(ispif); + if (prev) + isp4if_dealloc_buffer_node(prev); + else + dev_err(dev, "fail null prev buf\n"); + + } else if (meta->preview.enabled) { + dev_err(dev, "fail bad preview status %u\n", + meta->preview.status); + } + + if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) + isp4sd_send_meta_info(isp_subdev, mc); + + dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id, + isp_subdev->sensor_info.status); +} + +static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4fw_resp resp; + + while (true) { + if (isp4if_f2h_resp(ispif, stream_id, &resp)) { + /* Re-enable the interrupt */ + isp4_intr_enable(isp_subdev, stream_id, true); + /* + * Recheck to see if there is a new response. + * To ensure that an in-flight interrupt is not lost, + * enabling the interrupt must occur _before_ checking + * for a new response, hence a memory barrier is needed. + * Disable the interrupt again if there was a new + * response. + */ + mb(); + if (likely(isp4if_f2h_resp(ispif, stream_id, &resp))) + break; + + isp4_intr_enable(isp_subdev, stream_id, false); + } + + switch (resp.resp_id) { + case ISP4FW_RESP_ID_CMD_DONE: + isp4sd_fw_resp_cmd_done(isp_subdev, stream_id, + &resp.param.cmd_done); + break; + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + isp4sd_fw_resp_frame_done(isp_subdev, stream_id, + &resp.param.frame_done); + break; + default: + dev_err(dev, "-><- fail respid (0x%x)\n", + resp.resp_id); + break; + } + } +} + +static s32 isp4sd_fw_resp_thread(void *context) +{ + struct isp4_subdev_thread_param *para = context; + struct isp4_subdev *isp_subdev = para->isp_subdev; + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[para->idx]; + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "[%u] fw resp thread started\n", para->idx); + while (true) { + wait_event_interruptible(thread_ctx->waitq, + thread_ctx->resp_ready); + thread_ctx->resp_ready = false; + + if (kthread_should_stop()) { + dev_dbg(dev, "[%u] fw resp thread quit\n", para->idx); + break; + } + + isp4sd_fw_resp_func(isp_subdev, para->idx); + } + + return 0; +} + +static int isp4sd_stop_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + if (thread_ctx->thread) { + kthread_stop(thread_ctx->thread); + thread_ctx->thread = NULL; + } + } + + return 0; +} + +static int isp4sd_start_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + isp_subdev->isp_resp_para[i].idx = i; + isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev; + init_waitqueue_head(&thread_ctx->waitq); + thread_ctx->resp_ready = false; + + thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread, + &isp_subdev->isp_resp_para[i], + isp4sd_thread_name[i]); + if (IS_ERR(thread_ctx->thread)) { + dev_err(dev, "create thread [%d] fail\n", i); + thread_ctx->thread = NULL; + isp4sd_stop_resp_proc_threads(isp_subdev); + return -EINVAL; + } + } + + return 0; +} + +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) { + dev_err(dev, "fail for stream still running\n"); + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (isp_subdev->irq_enabled) { + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + disable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = false; + } + + isp4sd_stop_resp_proc_threads(isp_subdev); + dev_dbg(dev, "isp_subdev stop resp proc threads suc\n"); + + isp4if_stop(ispif); + + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) + dev_err(dev, + "fail to set isp_subdev performance state %u,ret %d\n", + perf_state, ret); + + /* hold ccpu reset */ + isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0); + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0); + ret = pm_runtime_put_sync(dev); + if (ret) + dev_err(dev, "power off isp_subdev fail %d\n", ret); + else + dev_dbg(dev, "power off isp_subdev suc\n"); + + ispif->status = ISP4IF_STATUS_PWR_OFF; + isp4if_clear_cmdq(ispif); + isp4sd_module_enable(isp_subdev, false); + + /* + * When opening the camera, isp4sd_module_enable(isp_subdev, true) is + * called. Hardware requires at least a 20ms delay between disabling + * and enabling the module, so a sleep is added to ensure ISP stability + * during quick reopen scenarios. + */ + msleep(20); + + return 0; +} + +int isp4sd_pwron_and_init(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (ispif->status == ISP4IF_STATUS_FW_RUNNING) { + dev_dbg(dev, "camera already opened, do nothing\n"); + return 0; + } + + isp4sd_module_enable(isp_subdev, true); + + if (ispif->status < ISP4IF_STATUS_PWR_ON) { + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH; + + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "fail to power on isp_subdev ret %d\n", + ret); + goto err_deinit; + } + + /* ISPPG ISP Power Status */ + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF); + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) { + dev_err(dev, + "fail to set performance state %u, ret %d\n", + perf_state, ret); + goto err_deinit; + } + + ispif->status = ISP4IF_STATUS_PWR_ON; + } + + isp_subdev->sensor_info.start_stream_cmd_sent = false; + isp_subdev->sensor_info.buf_sent_cnt = 0; + + ret = isp4if_start(ispif); + if (ret) { + dev_err(dev, "fail to start isp_subdev interface\n"); + goto err_deinit; + } + + if (isp4sd_start_resp_proc_threads(isp_subdev)) { + dev_err(dev, "isp_start_resp_proc_threads fail\n"); + goto err_deinit; + } + + dev_dbg(dev, "create resp threads ok\n"); + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + enable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = true; + + return 0; +err_deinit: + isp4sd_pwroff_and_deinit(sd); + return -EINVAL; +} + +static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + guard(mutex)(&isp_subdev->ops_mutex); + dev_dbg(dev, "status %i\n", output_info->start_status); + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) { + struct isp4fw_cmd_enable_out_ch cmd_ch_disable; + int ret; + + /* + * The struct will be shared with ISP FW, use memset() to + * guarantee padding bits are zeroed, since this is not + * guaranteed on all compilers. + */ + memset(&cmd_ch_disable, 0, sizeof(cmd_ch_disable)); + cmd_ch_disable.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + /* `cmd_ch_disable.is_enable` is already false */ + ret = isp4if_send_command_sync(ispif, + ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_disable, + sizeof(cmd_ch_disable)); + if (ret) + dev_err(dev, "fail to disable stream\n"); + else + dev_dbg(dev, "wait disable stream suc\n"); + + ret = isp4if_send_command_sync(ispif, ISP4FW_CMD_ID_STOP_STREAM, + NULL, 0); + if (ret) + dev_err(dev, "fail to stop stream\n"); + else + dev_dbg(dev, "wait stop stream suc\n"); + } + + isp4sd_uninit_stream(isp_subdev, state, pad); + + /* + * Return success to ensure the stop process proceeds, + * and disregard any errors since they are not fatal. + */ + return 0; +} + +static int isp4sd_start_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail, bad fsm %d\n", ispif->status); + return -EINVAL; + } + + switch (output_info->start_status) { + case ISP4SD_START_STATUS_OFF: + break; + case ISP4SD_START_STATUS_STARTED: + dev_dbg(dev, "stream already started, do nothing\n"); + return 0; + case ISP4SD_START_STATUS_START_FAIL: + dev_err(dev, "stream previously failed to start\n"); + return -EINVAL; + } + + ret = isp4sd_init_stream(isp_subdev); + if (ret) { + dev_err(dev, "fail to init isp_subdev stream\n"); + goto err_stop_stream; + } + + ret = isp4sd_setup_output(isp_subdev, state, pad); + if (ret) { + dev_err(dev, "fail to setup output\n"); + goto err_stop_stream; + } + + return 0; + +err_stop_stream: + isp4sd_stop_stream(isp_subdev, state, pad); + return ret; +} + +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_img_buf_node *buf_node; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail send img buf for bad fsm %d\n", + ispif->status); + return -EINVAL; + } + + buf_node = isp4if_alloc_buffer_node(buf_info); + if (!buf_node) { + dev_err(dev, "fail alloc sys img buf info node\n"); + return -ENOMEM; + } + + ret = isp4if_queue_buffer(ispif, buf_node); + if (ret) { + dev_err(dev, "fail to queue image buf, %d\n", ret); + goto error_release_buf_node; + } + + if (!isp_subdev->sensor_info.start_stream_cmd_sent) { + isp_subdev->sensor_info.buf_sent_cnt++; + + if (isp_subdev->sensor_info.buf_sent_cnt >= + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + ret = isp4if_send_command(ispif, + ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to START_STREAM"); + goto error_release_buf_node; + } + isp_subdev->sensor_info.start_stream_cmd_sent = true; + isp_subdev->sensor_info.output_info.start_status = + ISP4SD_START_STATUS_STARTED; + isp_subdev->sensor_info.status = + ISP4SD_START_STATUS_STARTED; + } else { + dev_dbg(dev, + "no send start, required %u, buf sent %u\n", + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM, + isp_subdev->sensor_info.buf_sent_cnt); + } + } + + return 0; + +error_release_buf_node: + isp4if_dealloc_buffer_node(buf_node); + return ret; +} + +static const struct v4l2_subdev_video_ops isp4sd_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static int isp4sd_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct isp4sd_output_info *stream_info = + &(to_isp4_subdev(sd)->sensor_info.output_info); + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); + + if (!fmt) { + dev_err(sd->dev, "fail to get state format\n"); + return -EINVAL; + } + + *fmt = format->format; + switch (fmt->code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + stream_info->image_size = fmt->width * fmt->height * 2; + break; + case MEDIA_BUS_FMT_YUYV8_1_5X8: + default: + stream_info->image_size = fmt->width * fmt->height * 3 / 2; + break; + } + + if (!stream_info->image_size) { + dev_err(sd->dev, + "fail set pad format,code 0x%x,width %u, height %u\n", + fmt->code, fmt->width, fmt->height); + return -EINVAL; + } + + dev_dbg(sd->dev, "set pad format suc, code:%x w:%u h:%u size:%u\n", + fmt->code, fmt->width, fmt->height, + stream_info->image_size); + + return 0; +} + +static int isp4sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_start_stream(isp_subdev, state, pad); +} + +static int isp4sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_stop_stream(isp_subdev, state, pad); +} + +static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = isp4sd_set_pad_format, + .enable_streams = isp4sd_enable_streams, + .disable_streams = isp4sd_disable_streams, +}; + +static const struct v4l2_subdev_ops isp4sd_subdev_ops = { + .video = &isp4sd_video_ops, + .pad = &isp4sd_pad_ops, +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = v4l2_dev->dev; + int ret; + + isp_subdev->dev = dev; + v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops); + isp_subdev->sdev.owner = THIS_MODULE; + isp_subdev->sdev.dev = dev; + snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name), "%s", + dev_name(dev)); + + isp_subdev->sdev.entity.name = isp4sd_entity_name; + isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + isp_subdev->sdev_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&isp_subdev->sdev.entity, 1, + &isp_subdev->sdev_pad); + if (ret) { + dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(&isp_subdev->sdev); + if (ret < 0) { + dev_err(dev, "fail to init finalize isp4 subdev %d\n", + ret); + return ret; + } + + ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev); + if (ret) { + dev_err(dev, "fail to register isp4 subdev to V4L2 device %d\n", + ret); + goto err_media_clean_up; + } + + isp4if_init(ispif, dev, isp_subdev->mmio); + + mutex_init(&isp_subdev->ops_mutex); + sensor_info->status = ISP4SD_START_STATUS_OFF; + + /* create ISP enable gpio control */ + isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev, + "enable_isp", + GPIOD_OUT_LOW); + if (IS_ERR(isp_subdev->enable_gpio)) { + ret = PTR_ERR(isp_subdev->enable_gpio); + dev_err(dev, "fail to get gpiod %d\n", ret); + goto err_subdev_unreg; + } + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + isp_subdev->irq[i] = irq[i]; + + isp_subdev->host2fw_seq_num = 1; + ispif->status = ISP4IF_STATUS_PWR_OFF; + + return 0; + +err_subdev_unreg: + v4l2_device_unregister_subdev(&isp_subdev->sdev); +err_media_clean_up: + v4l2_subdev_cleanup(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + return ret; +} + +void isp4sd_deinit(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + + v4l2_device_unregister_subdev(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + isp4if_deinit(ispif); + isp4sd_module_enable(isp_subdev, false); + + ispif->status = ISP4IF_STATUS_PWR_OFF; +} diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h new file mode 100644 index 00000000000000..ceade9fec5d93f --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_SUBDEV_H_ +#define _ISP4_SUBDEV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +/* + * One is for none sensor specific response which is not used now. + * Another is for sensor specific response + */ +#define ISP4SD_MAX_FW_RESP_STREAM_NUM 2 + +/* Indicates the ISP status */ +enum isp4sd_status { + ISP4SD_STATUS_PWR_OFF, + ISP4SD_STATUS_PWR_ON, + ISP4SD_STATUS_FW_RUNNING, + ISP4SD_STATUS_MAX +}; + +/* Indicates sensor and output stream status */ +enum isp4sd_start_status { + ISP4SD_START_STATUS_OFF, + ISP4SD_START_STATUS_STARTED, + ISP4SD_START_STATUS_START_FAIL, +}; + +struct isp4sd_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +/* This is ISP output after processing Bayer raw sensor input */ +struct isp4sd_output_info { + enum isp4sd_start_status start_status; + u32 image_size; +}; + +/* + * Struct for sensor info used as ISP input or source. + * status: sensor status. + * output_info: ISP output after processing the sensor input. + * start_stream_cmd_sent: indicates if ISP4FW_CMD_ID_START_STREAM was sent + * to firmware. + * buf_sent_cnt: number of buffers sent to receive images. + */ +struct isp4sd_sensor_info { + struct isp4sd_output_info output_info; + enum isp4sd_start_status status; + bool start_stream_cmd_sent; + u32 buf_sent_cnt; +}; + +/* + * The thread is created by the driver to handle firmware responses which will + * be waken up when a firmware-to-driver response interrupt occurs. + */ +struct isp4sd_thread_handler { + struct task_struct *thread; + wait_queue_head_t waitq; + bool resp_ready; +}; + +struct isp4_subdev_thread_param { + u32 idx; + struct isp4_subdev *isp_subdev; +}; + +struct isp4_subdev { + struct v4l2_subdev sdev; + struct isp4_interface ispif; + + struct media_pad sdev_pad; + + enum isp4sd_status isp_status; + /* mutex used to synchronize the operation with firmware */ + struct mutex ops_mutex; + + struct isp4sd_thread_handler + fw_resp_thread[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + + u32 host2fw_seq_num; + + struct isp4sd_sensor_info sensor_info; + + /* gpio descriptor */ + struct gpio_desc *enable_gpio; + struct device *dev; + void __iomem *mmio; + struct isp4_subdev_thread_param + isp_resp_para[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + bool irq_enabled; + /* spin lock to access ISP_SYS_INT0_EN exclusively */ + spinlock_t irq_lock; +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]); +void isp4sd_deinit(struct isp4_subdev *isp_subdev); +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info); +int isp4sd_pwron_and_init(struct v4l2_subdev *sd); +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd); + +#endif /* _ISP4_SUBDEV_H_ */ From 00441ceee7435bc8a2b3da5ccb794cec49bca4a5 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Fri, 20 Mar 2026 16:41:44 +0800 Subject: [PATCH 167/521] FROMLIST: media: platform: amd: isp4 video node and buffers handling added Isp video implements v4l2 video interface and supports NV12 and YUYV. It manages buffers, pipeline power and state. Cherry-picked Sultan's DMA buffer related fix from branch v6.16-drm-tip-isp4-for-amd on https://github.com/kerneltoast/kernel_x86_laptop.git Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Link: https://lore.kernel.org/all/20260320084146.200988-1-Bin.Du@amd.com/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 11 + drivers/media/platform/amd/isp4/isp4_subdev.c | 13 +- drivers/media/platform/amd/isp4/isp4_subdev.h | 2 + drivers/media/platform/amd/isp4/isp4_video.c | 797 ++++++++++++++++++ drivers/media/platform/amd/isp4/isp4_video.h | 57 ++ 7 files changed, 881 insertions(+), 4 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_video.c create mode 100644 drivers/media/platform/amd/isp4/isp4_video.h diff --git a/MAINTAINERS b/MAINTAINERS index c8982c39530fd4..44bc8475180b0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1177,6 +1177,8 @@ F: drivers/media/platform/amd/isp4/isp4_interface.c F: drivers/media/platform/amd/isp4/isp4_interface.h F: drivers/media/platform/amd/isp4/isp4_subdev.c F: drivers/media/platform/amd/isp4/isp4_subdev.h +F: drivers/media/platform/amd/isp4/isp4_video.c +F: drivers/media/platform/amd/isp4/isp4_video.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 7e4854b6a4cd60..3fa0ee6d8a96c9 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ isp4_interface.o \ - isp4_subdev.o + isp4_subdev.o \ + isp4_video.o \ No newline at end of file diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index e62f5d652d8138..9480dfffbcb242 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -173,6 +173,17 @@ static int isp4_capture_probe(struct platform_device *pdev) goto err_pm_disable; } + ret = media_create_pad_link(&isp_dev->isp_subdev.sdev.entity, + 0, + &isp_dev->isp_subdev.isp_vdev.vdev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err_probe(dev, ret, "fail to create pad link\n"); + goto err_isp4_deinit; + } + ret = media_device_register(&isp_dev->mdev); if (ret) { dev_err_probe(dev, ret, "fail to register media device\n"); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 6d571b7f8840df..5202232d50c560 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -467,11 +467,13 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); - if (prev) + if (prev) { + isp4vid_handle_frame_done(&isp_subdev->isp_vdev, + &prev->buf_info); isp4if_dealloc_buffer_node(prev); - else + } else { dev_err(dev, "fail null prev buf\n"); - + } } else if (meta->preview.enabled) { dev_err(dev, "fail bad preview status %u\n", meta->preview.status); @@ -1006,6 +1008,10 @@ int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, isp_subdev->host2fw_seq_num = 1; ispif->status = ISP4IF_STATUS_PWR_OFF; + ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev); + if (ret) + goto err_subdev_unreg; + return 0; err_subdev_unreg: @@ -1020,6 +1026,7 @@ void isp4sd_deinit(struct isp4_subdev *isp_subdev) { struct isp4_interface *ispif = &isp_subdev->ispif; + isp4vid_dev_deinit(&isp_subdev->isp_vdev); v4l2_device_unregister_subdev(&isp_subdev->sdev); media_entity_cleanup(&isp_subdev->sdev.entity); isp4if_deinit(ispif); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h index ceade9fec5d93f..ddf6bdf4a62ab1 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.h +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -17,6 +17,7 @@ #include "isp4_fw_cmd_resp.h" #include "isp4_hw_reg.h" #include "isp4_interface.h" +#include "isp4_video.h" /* * One is for none sensor specific response which is not used now. @@ -83,6 +84,7 @@ struct isp4_subdev_thread_param { struct isp4_subdev { struct v4l2_subdev sdev; struct isp4_interface ispif; + struct isp4vid_dev isp_vdev; struct media_pad sdev_pad; diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c new file mode 100644 index 00000000000000..0cebb39f98e1da --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "isp4_interface.h" +#include "isp4_subdev.h" +#include "isp4_video.h" + +#define ISP4VID_ISP_DRV_NAME "amd_isp_capture" +#define ISP4VID_MAX_PREVIEW_FPS 30 +#define ISP4VID_DEFAULT_FMT V4L2_PIX_FMT_NV12 + +#define ISP4VID_PAD_VIDEO_OUTPUT 0 + +/* time perframe default */ +#define ISP4VID_ISP_TPF_DEFAULT isp4vid_tpfs[0] + +static const char *const isp4vid_video_dev_name = "Preview"; + +/* Sizes must be in increasing order */ +static const struct v4l2_frmsize_discrete isp4vid_frmsize[] = { + {640, 360}, + {640, 480}, + {1280, 720}, + {1280, 960}, + {1920, 1080}, + {1920, 1440}, + {2560, 1440}, + {2880, 1620}, + {2880, 1624}, + {2888, 1808}, +}; + +static const u32 isp4vid_formats[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUYV +}; + +/* time perframe list */ +static const struct v4l2_fract isp4vid_tpfs[] = { + { 1, ISP4VID_MAX_PREVIEW_FPS } +}; + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf) +{ + struct isp4vid_capture_buffer *isp4vid_buf; + void *vbuf; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + isp4vid_buf = list_first_entry_or_null(&isp_vdev->buf_list, + typeof(*isp4vid_buf), + list); + if (!isp4vid_buf) + return; + + vbuf = vb2_plane_vaddr(&isp4vid_buf->vb2.vb2_buf, 0); + + if (vbuf != img_buf->planes[0].sys_addr) { + dev_err(isp_vdev->dev, "Invalid vbuf\n"); + return; + } + + list_del(&isp4vid_buf->list); + } + + /* Fill the buffer */ + isp4vid_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); + isp4vid_buf->vb2.sequence = isp_vdev->sequence++; + isp4vid_buf->vb2.field = V4L2_FIELD_ANY; + + vb2_set_plane_payload(&isp4vid_buf->vb2.vb2_buf, + 0, isp_vdev->format.sizeimage); + + vb2_buffer_done(&isp4vid_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(size=%u)\n", + isp_vdev->format.sizeimage); +} + +static const struct v4l2_pix_format isp4vid_fmt_default = { + .width = 1920, + .height = 1080, + .pixelformat = ISP4VID_DEFAULT_FMT, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, +}; + +static void isp4vid_capture_return_all_buffers(struct isp4vid_dev *isp_vdev, + enum vb2_buffer_state state) +{ + struct isp4vid_capture_buffer *vbuf, *node; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + list_for_each_entry_safe(vbuf, node, &isp_vdev->buf_list, list) + vb2_buffer_done(&vbuf->vb2.vb2_buf, state); + INIT_LIST_HEAD(&isp_vdev->buf_list); + } + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(%d)\n", state); +} + +static int isp4vid_vdev_link_validate(struct media_link *link) +{ + return 0; +} + +static const struct media_entity_operations isp4vid_vdev_ent_ops = { + .link_validate = isp4vid_vdev_link_validate, +}; + +static const struct v4l2_file_operations isp4vid_vdev_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int isp4vid_ioctl_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + strscpy(cap->driver, ISP4VID_ISP_DRV_NAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), "%s", ISP4VID_ISP_DRV_NAME); + cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + + dev_dbg(isp_vdev->dev, "%s|capabilities=0x%X\n", isp_vdev->vdev.name, + cap->capabilities); + + return 0; +} + +static int isp4vid_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + f->fmt.pix = isp_vdev->format; + + return 0; +} + +static int isp4vid_fill_buffer_size(struct v4l2_pix_format *fmt) +{ + int ret = 0; + + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt->bytesperline = fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height * 3 / 2; + break; + case V4L2_PIX_FMT_YUYV: + fmt->bytesperline = fmt->width * 2; + fmt->sizeimage = fmt->bytesperline * fmt->height; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int isp4vid_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + struct v4l2_pix_format *format = &f->fmt.pix; + const struct v4l2_frmsize_discrete *fsz; + size_t i; + + /* + * Check if the hardware supports the requested format, use the default + * format otherwise. + */ + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == format->pixelformat) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + format->pixelformat = ISP4VID_DEFAULT_FMT; + + switch (format->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: + fsz = v4l2_find_nearest_size(isp4vid_frmsize, + ARRAY_SIZE(isp4vid_frmsize), + width, height, format->width, + format->height); + format->width = fsz->width; + format->height = fsz->height; + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, + format->pixelformat); + return -EINVAL; + } + + /* + * There is no need to check the return value, as failure will never + * happen here + */ + isp4vid_fill_buffer_size(format); + + if (format->field == V4L2_FIELD_ANY) + format->field = isp4vid_fmt_default.field; + + if (format->colorspace == V4L2_COLORSPACE_DEFAULT) + format->colorspace = isp4vid_fmt_default.colorspace; + + return 0; +} + +static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev, + struct v4l2_pix_format *pix_fmt) +{ + struct v4l2_subdev_format fmt = {}; + + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1_5X8; + break; + case V4L2_PIX_FMT_YUYV: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1X16; + break; + default: + return -EINVAL; + } + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = ISP4VID_PAD_VIDEO_OUTPUT; + fmt.format.width = pix_fmt->width; + fmt.format.height = pix_fmt->height; + return v4l2_subdev_call(sdev, pad, set_fmt, NULL, &fmt); +} + +static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + int ret; + + /* Do not change the format while stream is on */ + if (vb2_is_busy(&isp_vdev->vbq)) + return -EBUSY; + + ret = isp4vid_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + dev_dbg(isp_vdev->dev, "%s|width height:%ux%u->%ux%u\n", + isp_vdev->vdev.name, + isp_vdev->format.width, isp_vdev->format.height, + f->fmt.pix.width, f->fmt.pix.height); + dev_dbg(isp_vdev->dev, "%s|pixelformat:0x%x-0x%x\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat, + f->fmt.pix.pixelformat); + dev_dbg(isp_vdev->dev, "%s|bytesperline:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.bytesperline, + f->fmt.pix.bytesperline); + dev_dbg(isp_vdev->dev, "%s|sizeimage:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.sizeimage, + f->fmt.pix.sizeimage); + + isp_vdev->format = f->fmt.pix; + ret = isp4vid_set_fmt_2_isp(isp_vdev->isp_sdev, &isp_vdev->format); + + return ret; +} + +static int isp4vid_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUYV; + break; + default: + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|index=%d, pixelformat=0x%X\n", + isp_vdev->vdev.name, f->index, f->pixelformat); + + return 0; +} + +static int isp4vid_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) { + if (isp4vid_formats[i] == fsize->pixel_format) + break; + } + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + if (fsize->index < ARRAY_SIZE(isp4vid_frmsize)) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete = isp4vid_frmsize[fsize->index]; + dev_dbg(isp_vdev->dev, "%s|size[%d]=%dx%d\n", + isp_vdev->vdev.name, fsize->index, + fsize->discrete.width, fsize->discrete.height); + } else { + return -EINVAL; + } + + return 0; +} + +static int isp4vid_ioctl_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + size_t i; + + if (fival->index >= ARRAY_SIZE(isp4vid_tpfs)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == fival->pixel_format) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_frmsize); i++) + if (isp4vid_frmsize[i].width == fival->width && + isp4vid_frmsize[i].height == fival->height) + break; + + if (i == ARRAY_SIZE(isp4vid_frmsize)) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = isp4vid_tpfs[fival->index]; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + + dev_dbg(isp_vdev->dev, "%s|interval[%d]=%d/%d\n", + isp_vdev->vdev.name, fival->index, + fival->discrete.numerator, + fival->discrete.denominator); + + return 0; +} + +static int isp4vid_ioctl_g_param(struct file *file, void *priv, + struct v4l2_streamparm *param) +{ + struct v4l2_captureparm *capture = ¶m->parm.capture; + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + capture->capability = V4L2_CAP_TIMEPERFRAME; + capture->timeperframe = isp_vdev->timeperframe; + capture->readbuffers = 0; + + dev_dbg(isp_vdev->dev, "%s|timeperframe=%d/%d\n", isp_vdev->vdev.name, + capture->timeperframe.numerator, + capture->timeperframe.denominator); + + return 0; +} + +static const struct v4l2_ioctl_ops isp4vid_vdev_ioctl_ops = { + .vidioc_querycap = isp4vid_ioctl_querycap, + .vidioc_enum_fmt_vid_cap = isp4vid_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = isp4vid_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = isp4vid_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = isp4vid_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_parm = isp4vid_ioctl_g_param, + .vidioc_s_parm = isp4vid_ioctl_g_param, + .vidioc_enum_framesizes = isp4vid_enum_framesizes, + .vidioc_enum_frameintervals = isp4vid_ioctl_enum_frameintervals, +}; + +static unsigned int isp4vid_get_image_size(struct v4l2_pix_format *fmt) +{ + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + return fmt->width * fmt->height * 3 / 2; + case V4L2_PIX_FMT_YUYV: + return fmt->width * fmt->height * 2; + default: + return 0; + } +} + +static int isp4vid_qops_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); + + if (*nplanes > 1) { + dev_err(isp_vdev->dev, + "fail to setup queue, no mplane supported %u\n", + *nplanes); + return -EINVAL; + } + + if (*nplanes == 1) { + unsigned int size; + + size = isp4vid_get_image_size(&isp_vdev->format); + if (sizes[0] < size) { + dev_err(isp_vdev->dev, + "fail for small plane size %u, %u expected\n", + sizes[0], size); + return -EINVAL; + } + } + + if (q_num_bufs + *nbuffers < ISP4IF_MAX_STREAM_BUF_COUNT) + *nbuffers = ISP4IF_MAX_STREAM_BUF_COUNT - q_num_bufs; + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: { + *nplanes = 1; + sizes[0] = max(sizes[0], isp_vdev->format.sizeimage); + isp_vdev->format.sizeimage = sizes[0]; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|*nbuffers=%u *nplanes=%u sizes[0]=%u\n", + isp_vdev->vdev.name, + *nbuffers, *nplanes, sizes[0]); + + return 0; +} + +static void isp4vid_qops_buffer_queue(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + struct isp4if_img_buf_info *img_buf = &buf->img_buf; + void *vaddr = vb2_plane_vaddr(vb, 0); + + dev_dbg(isp_vdev->dev, "queue buf, vaddr %p, gpuva 0x%llx, size %u\n", + vaddr, buf->gpu_addr, vb->planes[0].length); + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: { + u32 y_size = isp_vdev->format.sizeimage / 3 * 2; + u32 uv_size = isp_vdev->format.sizeimage / 3; + + img_buf->planes[0].len = y_size; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = uv_size; + img_buf->planes[1].sys_addr = vaddr + y_size; + img_buf->planes[1].mc_addr = buf->gpu_addr + y_size; + + dev_dbg(isp_vdev->dev, "img_buf[1]: mc=0x%llx size=%u\n", + img_buf->planes[1].mc_addr, + img_buf->planes[1].len); + + img_buf->planes[2].len = 0; + } + break; + case V4L2_PIX_FMT_YUYV: { + img_buf->planes[0].len = isp_vdev->format.sizeimage; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = 0; + img_buf->planes[2].len = 0; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return; + } + + if (isp_vdev->stream_started) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, img_buf); + + scoped_guard(mutex, &isp_vdev->buf_list_lock) + list_add_tail(&buf->list, &isp_vdev->buf_list); +} + +static int isp4vid_qops_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct isp4vid_capture_buffer *isp4vid_buf; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret = 0; + + isp_vdev->sequence = 0; + + ret = isp4sd_pwron_and_init(isp_vdev->isp_sdev); + if (ret) { + dev_err(isp_vdev->dev, "power up isp fail %d\n", ret); + goto release_buffers; + } + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n", + subdev->name, ret); + goto release_buffers; + } + } + + list_for_each_entry(isp4vid_buf, &isp_vdev->buf_list, list) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, + &isp4vid_buf->img_buf); + + isp_vdev->stream_started = true; + + return 0; + +release_buffers: + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void isp4vid_qops_stop_streaming(struct vb2_queue *vq) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_dbg(isp_vdev->dev, "fail stop streaming: %s %d\n", + subdev->name, ret); + } + + isp_vdev->stream_started = false; + isp4sd_pwroff_and_deinit(isp_vdev->isp_sdev); + + /* Release all active buffers */ + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_ERROR); +} + +static int isp4vid_qops_buf_init(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + void *mem_priv = vb->planes[0].mem_priv; + struct device *dev = isp_vdev->dev; + u64 gpu_addr; + void *bo; + int ret; + + if (vb->planes[0].dbuf) { + buf->dbuf = vb->planes[0].dbuf; + } else { + /* + * HAS_DMA is a Kconfig dependency so CONFIG_HAS_DMA is always + * defined when this driver is compiled. The #else branch is + * kept as a safeguard in case the dependency is ever removed. + */ +#ifdef CONFIG_HAS_DMA + buf->dbuf = vb2_vmalloc_memops.get_dmabuf(vb, mem_priv, 0); + if (IS_ERR_OR_NULL(buf->dbuf)) { + dev_err(dev, "fail to get dma buf\n"); + return -EINVAL; + } +#else + dev_err(dev, "get dmabuf fail -- CONFIG_HAS_DMA not defined\n"); + buf->dbuf = NULL; + return -EINVAL; +#endif + } + + /* create isp user BO and obtain gpu_addr */ + ret = isp_user_buffer_alloc(dev, buf->dbuf, &bo, &gpu_addr); + if (ret) { + dev_err(dev, "fail to create isp user BO\n"); + if (!vb->planes[0].dbuf) { + dma_buf_put(buf->dbuf); + buf->dbuf = NULL; + } + + return ret; + } + + buf->bo = bo; + buf->gpu_addr = gpu_addr; + return 0; +} + +static void isp4vid_qops_buf_cleanup(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + + if (buf->bo) { + isp_user_buffer_free(buf->bo); + buf->bo = NULL; + } + + /* + * Only put dmabufs we obtained ourselves via get_dmabuf, not ones + * provided by the framework for DMABUF import + */ + if (buf->dbuf && buf->dbuf != vb->planes[0].dbuf) + dma_buf_put(buf->dbuf); + + buf->dbuf = NULL; +} + +static const struct vb2_ops isp4vid_qops = { + .queue_setup = isp4vid_qops_queue_setup, + .buf_init = isp4vid_qops_buf_init, + .buf_cleanup = isp4vid_qops_buf_cleanup, + .start_streaming = isp4vid_qops_start_streaming, + .stop_streaming = isp4vid_qops_stop_streaming, + .buf_queue = isp4vid_qops_buffer_queue, +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd) +{ + const char *vdev_name = isp4vid_video_dev_name; + struct v4l2_device *v4l2_dev; + struct video_device *vdev; + struct vb2_queue *q; + int ret; + + if (!isp_vdev || !isp_sd || !isp_sd->v4l2_dev) + return -EINVAL; + + v4l2_dev = isp_sd->v4l2_dev; + vdev = &isp_vdev->vdev; + + isp_vdev->isp_sdev = isp_sd; + isp_vdev->dev = v4l2_dev->dev; + + /* Initialize the vb2_queue struct */ + mutex_init(&isp_vdev->vbq_lock); + q = &isp_vdev->vbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct isp4vid_capture_buffer); + q->min_queued_buffers = 2; + q->ops = &isp4vid_qops; + q->drv_priv = isp_vdev; + q->mem_ops = &vb2_vmalloc_memops; + q->lock = &isp_vdev->vbq_lock; + q->dev = v4l2_dev->dev; + ret = vb2_queue_init(q); + if (ret) { + dev_err(v4l2_dev->dev, "vb2_queue_init error:%d\n", ret); + return ret; + } + + /* Initialize buffer list and its lock */ + mutex_init(&isp_vdev->buf_list_lock); + INIT_LIST_HEAD(&isp_vdev->buf_list); + + /* Set default frame format */ + isp_vdev->format = isp4vid_fmt_default; + isp_vdev->timeperframe = ISP4VID_ISP_TPF_DEFAULT; + v4l2_simplify_fraction(&isp_vdev->timeperframe.numerator, + &isp_vdev->timeperframe.denominator, 8, 333); + + ret = isp4vid_fill_buffer_size(&isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail to fill buffer size: %d\n", ret); + goto err_release_vb2_queue; + } + + ret = isp4vid_set_fmt_2_isp(isp_sd, &isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail init format :%d\n", ret); + goto err_release_vb2_queue; + } + + /* Initialize the video_device struct */ + isp_vdev->vdev.entity.name = vdev_name; + isp_vdev->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + isp_vdev->vdev_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&isp_vdev->vdev.entity, 1, + &isp_vdev->vdev_pad); + + if (ret) { + dev_err(v4l2_dev->dev, "init media entity pad fail:%d\n", ret); + goto err_release_vb2_queue; + } + + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &isp4vid_vdev_ent_ops; + vdev->release = video_device_release_empty; + vdev->fops = &isp4vid_vdev_fops; + vdev->ioctl_ops = &isp4vid_vdev_ioctl_ops; + vdev->lock = NULL; + vdev->queue = q; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + video_set_drvdata(vdev, isp_vdev); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(v4l2_dev->dev, "register video device fail:%d\n", ret); + goto err_entity_cleanup; + } + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&isp_vdev->vdev.entity); +err_release_vb2_queue: + vb2_queue_release(q); + return ret; +} + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev) +{ + vb2_video_unregister_device(&isp_vdev->vdev); +} diff --git a/drivers/media/platform/amd/isp4/isp4_video.h b/drivers/media/platform/amd/isp4/isp4_video.h new file mode 100644 index 00000000000000..c66451e2616644 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_VIDEO_H_ +#define _ISP4_VIDEO_H_ + +#include +#include + +#include "isp4_interface.h" + +struct isp4vid_capture_buffer { + /* + * struct vb2_v4l2_buffer must be the first element + * the videobuf2 framework will allocate this struct based on + * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of + * memory as a vb2_buffer + */ + struct vb2_v4l2_buffer vb2; + struct isp4if_img_buf_info img_buf; + struct list_head list; + struct dma_buf *dbuf; + void *bo; + u64 gpu_addr; +}; + +struct isp4vid_dev { + struct video_device vdev; + struct media_pad vdev_pad; + struct v4l2_pix_format format; + + /* mutex that protects vbq */ + struct mutex vbq_lock; + struct vb2_queue vbq; + + /* mutex that protects buf_list */ + struct mutex buf_list_lock; + struct list_head buf_list; + + u32 sequence; + bool stream_started; + + struct device *dev; + struct v4l2_subdev *isp_sdev; + struct v4l2_fract timeperframe; +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd); + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev); + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf); + +#endif /* _ISP4_VIDEO_H_ */ From 550d0c925e0ec10dd62d656742e31a8641272721 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Fri, 20 Mar 2026 16:41:45 +0800 Subject: [PATCH 168/521] FROMLIST: media: platform: amd: isp4 debug fs logging and more descriptive errors Add debug fs for isp4 driver and add more detailed descriptive error info to some of the log message Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Link: https://lore.kernel.org/all/20260320084146.200988-1-Bin.Du@amd.com/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 4 + drivers/media/platform/amd/isp4/isp4_debug.c | 271 ++++++++++++++++++ drivers/media/platform/amd/isp4/isp4_debug.h | 41 +++ .../media/platform/amd/isp4/isp4_interface.c | 25 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 29 +- drivers/media/platform/amd/isp4/isp4_subdev.h | 5 + 8 files changed, 360 insertions(+), 20 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.c create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.h diff --git a/MAINTAINERS b/MAINTAINERS index 44bc8475180b0b..7e148de38296ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1171,6 +1171,8 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_debug.c +F: drivers/media/platform/amd/isp4/isp4_debug.h F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h F: drivers/media/platform/amd/isp4/isp4_interface.c diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 3fa0ee6d8a96c9..3849062e17f3d0 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ + isp4_debug.o \ isp4_interface.o \ isp4_subdev.o \ - isp4_video.o \ No newline at end of file + isp4_video.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 9480dfffbcb242..bf6b8e26c2c0fb 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -9,6 +9,7 @@ #include #include "isp4.h" +#include "isp4_debug.h" #include "isp4_hw_reg.h" #define ISP4_DRV_NAME "amd_isp_capture" @@ -191,6 +192,7 @@ static int isp4_capture_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, isp_dev); + isp_debugfs_create(isp_dev); return 0; @@ -210,6 +212,8 @@ static void isp4_capture_remove(struct platform_device *pdev) struct isp4_device *isp_dev = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + isp_debugfs_remove(isp_dev); + media_device_unregister(&isp_dev->mdev); isp4sd_deinit(&isp_dev->isp_subdev); pm_runtime_disable(dev); diff --git a/drivers/media/platform/amd/isp4/isp4_debug.c b/drivers/media/platform/amd/isp4/isp4_debug.c new file mode 100644 index 00000000000000..2fc00fc9a19403 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include "isp4.h" +#include "isp4_debug.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024) +#define ISP4DBG_MACRO_2_STR(X) #X +#define ISP4DBG_ONE_TIME_LOG_LEN 510 + +#ifdef CONFIG_DEBUG_FS + +void isp_debugfs_create(struct isp4_device *isp_dev) +{ + isp_dev->isp_subdev.debugfs_dir = debugfs_create_dir("amd_isp4", NULL); + debugfs_create_bool("fw_log_enable", 0644, + isp_dev->isp_subdev.debugfs_dir, + &isp_dev->isp_subdev.enable_fw_log); + isp_dev->isp_subdev.fw_log_output = + devm_kzalloc(isp_dev->isp_subdev.dev, + ISP4DBG_FW_LOG_RINGBUF_SIZE + 32, + GFP_KERNEL); +} + +void isp_debugfs_remove(struct isp4_device *isp_dev) +{ + debugfs_remove_recursive(isp_dev->isp_subdev.debugfs_dir); + isp_dev->isp_subdev.debugfs_dir = NULL; +} + +static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, void *sys, u32 rb_size) +{ + struct isp4_interface *ispif = &isp->ispif; + char *buf = isp->fw_log_output; + struct device *dev = isp->dev; + u32 rd_ptr, wr_ptr; + u32 total_cnt = 0; + u32 offset = 0; + u32 cnt; + + if (!sys || !rb_size) + return 0; + + guard(mutex)(&ispif->isp4if_mutex); + + rd_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_RPTR0); + wr_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_WPTR0); + + do { + if (wr_ptr > rd_ptr) + cnt = wr_ptr - rd_ptr; + else if (wr_ptr < rd_ptr) + cnt = rb_size - rd_ptr; + else + goto quit; + + if (cnt > rb_size) { + dev_err(dev, "fail bad fw log size %u\n", cnt); + goto quit; + } + + memcpy(buf + offset, sys + rd_ptr, cnt); + + offset += cnt; + total_cnt += cnt; + rd_ptr = (rd_ptr + cnt) % rb_size; + } while (rd_ptr < wr_ptr); + + isp4hw_wreg(isp->mmio, ISP_LOG_RB_RPTR0, rd_ptr); + +quit: + return total_cnt; +} + +void isp_fw_log_print(struct isp4_subdev *isp) +{ + struct isp4_interface *ispif = &isp->ispif; + char *fw_log_buf = isp->fw_log_output; + u32 cnt; + + if (!isp->enable_fw_log || !fw_log_buf) + return; + + cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr, + ispif->fw_log_buf->mem_size); + + if (cnt) { + char temp_ch; + char *str; + char *end; + /* line end */ + char *le; + + str = (char *)fw_log_buf; + end = ((char *)fw_log_buf + cnt); + fw_log_buf[cnt] = 0; + + while (str < end) { + le = strchr(str, 0x0A); + if ((le && str + ISP4DBG_ONE_TIME_LOG_LEN >= le) || + (!le && str + ISP4DBG_ONE_TIME_LOG_LEN >= end)) { + if (le) + *le = 0; + + if (*str != '\0') + dev_dbg(isp->dev, "%s", str); + + if (le) { + *le = 0x0A; + str = le + 1; + } else { + break; + } + } else { + u32 tmp_len = ISP4DBG_ONE_TIME_LOG_LEN; + + temp_ch = str[tmp_len]; + str[tmp_len] = 0; + dev_dbg(isp->dev, "%s", str); + str[tmp_len] = temp_ch; + str = &str[tmp_len]; + } + } + } +} +#endif + +char *isp4dbg_get_buf_src_str(u32 src) +{ + switch (src) { + case ISP4FW_BUFFER_SOURCE_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_SOURCE_STREAM); + default: + return "Unknown buf source"; + } +} + +char *isp4dbg_get_buf_done_str(u32 status) +{ + switch (status) { + case ISP4FW_BUFFER_STATUS_INVALID: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_INVALID); + case ISP4FW_BUFFER_STATUS_SKIPPED: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_SKIPPED); + case ISP4FW_BUFFER_STATUS_EXIST: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_EXIST); + case ISP4FW_BUFFER_STATUS_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DONE); + case ISP4FW_BUFFER_STATUS_LACK: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_LACK); + case ISP4FW_BUFFER_STATUS_DIRTY: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DIRTY); + case ISP4FW_BUFFER_STATUS_MAX: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_MAX); + default: + return "Unknown Buf Done Status"; + } +} + +char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */) +{ + switch (fmt) { + case ISP4FW_IMAGE_FORMAT_NV12: + return "NV12"; + case ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED: + return "YUV422INTERLEAVED"; + default: + return "unknown fmt"; + } +} + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, + void *in, void *orig_buf) +{ + struct isp4fw_buffer_meta_info *p; + struct isp4if_img_buf_info *orig; + + if (!in) + return; + + if (!pre) + pre = ""; + + p = in; + orig = orig_buf; + + dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre, + isp4dbg_get_img_fmt_str(p->image_prop.image_format), + p->enabled, isp4dbg_get_buf_done_str(p->status), p->status, + isp4dbg_get_buf_src_str(p->source)); + + dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n", + orig->planes[0].sys_addr, orig->planes[0].mc_addr, + orig->planes[0].len, orig->planes[1].sys_addr, + orig->planes[1].mc_addr, orig->planes[1].len, + orig->planes[2].sys_addr, orig->planes[2].mc_addr, + orig->planes[2].len); +} + +char *isp4dbg_get_buf_type(u32 type) +{ + /* enum isp4fw_buffer_type */ + switch (type) { + case ISP4FW_BUFFER_TYPE_PREVIEW: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_PREVIEW); + case ISP4FW_BUFFER_TYPE_META_INFO: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_META_INFO); + case ISP4FW_BUFFER_TYPE_MEM_POOL: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_MEM_POOL); + default: + return "unknown type"; + } +} + +char *isp4dbg_get_cmd_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_CMD_ID_START_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_START_STREAM); + case ISP4FW_CMD_ID_STOP_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_STOP_STREAM); + case ISP4FW_CMD_ID_SEND_BUFFER: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SEND_BUFFER); + case ISP4FW_CMD_ID_SET_STREAM_CONFIG: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_STREAM_CONFIG); + case ISP4FW_CMD_ID_SET_OUT_CHAN_PROP: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_OUT_CHAN_PROP); + case ISP4FW_CMD_ID_ENABLE_OUT_CHAN: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_ENABLE_OUT_CHAN); + default: + return "unknown cmd"; + } +} + +char *isp4dbg_get_resp_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_RESP_ID_CMD_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_CMD_DONE); + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_NOTI_FRAME_DONE); + default: + return "unknown respid"; + } +} + +char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */) +{ + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + return "STREAM_GLOBAL"; + case ISP4IF_STREAM_ID_1: + return "STREAM1"; + default: + return "unknown streamID"; + } +} + +char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */) +{ + switch ((enum isp4fw_pipe_out_ch)ch) { + case ISP4FW_ISP_PIPE_OUT_CH_PREVIEW: + return "prev"; + default: + return "unknown channel"; + } +} diff --git a/drivers/media/platform/amd/isp4/isp4_debug.h b/drivers/media/platform/amd/isp4/isp4_debug.h new file mode 100644 index 00000000000000..d1262e03ae6405 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_DEBUG_H_ +#define _ISP4_DEBUG_H_ + +#include +#include + +#include "isp4_subdev.h" + +#ifdef CONFIG_DEBUG_FS +struct isp4_device; + +void isp_debugfs_create(struct isp4_device *isp_dev); +void isp_debugfs_remove(struct isp4_device *isp_dev); +void isp_fw_log_print(struct isp4_subdev *isp); + +#else + +/* to avoid checkpatch warning */ +#define isp_debugfs_create(cam) ((void)(cam)) +#define isp_debugfs_remove(cam) ((void)(cam)) +#define isp_fw_log_print(isp) ((void)(isp)) + +#endif /* CONFIG_DEBUG_FS */ + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, void *p, + void *orig_buf /* struct sys_img_buf_handle */); +char *isp4dbg_get_img_fmt_str(int fmt /* enum _image_format_t */); +char *isp4dbg_get_out_ch_str(int ch /* enum _isp_pipe_out_ch_t */); +char *isp4dbg_get_cmd_str(u32 cmd); +char *isp4dbg_get_buf_type(u32 type);/* enum _buffer_type_t */ +char *isp4dbg_get_resp_str(u32 resp); +char *isp4dbg_get_buf_src_str(u32 src); +char *isp4dbg_get_buf_done_str(u32 status); +char *isp4dbg_get_if_stream_str(u32 stream); + +#endif /* _ISP4_DEBUG_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index c35ff5c7273e79..4324d4adbcffa6 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -5,6 +5,7 @@ #include +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_hw_reg.h" #include "isp4_interface.h" @@ -302,8 +303,9 @@ static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, wr_ptr = isp4hw_rreg(ispif->mmio, wreg); if (rd_ptr >= len || wr_ptr >= len) { dev_err(dev, - "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, cmd_sz=%u\n", - stream, rd_ptr, wr_ptr, len, cmd_sz); + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, cmd_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, + wr_ptr, len, cmd_sz); return -EINVAL; } @@ -394,8 +396,9 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, rb_config->reg_wptr); dev_err(dev, - "fail to get free cmdq slot, stream (%d),rd %u, wr %u\n", - stream, rd_ptr, wr_ptr); + "failed to get free cmdq slot, stream %s(%d),rd %u, wr %u\n", + isp4dbg_get_if_stream_str(stream), stream, + rd_ptr, wr_ptr); ret = -ETIMEDOUT; goto free_ele; } @@ -421,8 +424,8 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); if (ret) { dev_err(dev, - "fail for insert_isp_fw_cmd cmd_id (0x%08x)\n", - cmd_id); + "fail for insert_isp_fw_cmd cmd_id %s(0x%08x)\n", + isp4dbg_get_cmd_str(cmd_id), cmd_id); goto err_dequeue_ele; } } @@ -686,8 +689,9 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, if (checksum != resp->resp_check_sum) { dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", checksum, resp->resp_check_sum, rd_ptr, wr_ptr); - dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", - stream, resp->resp_seq_num, + dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n", + isp4dbg_get_if_stream_str(stream), stream, + resp->resp_seq_num, isp4dbg_get_resp_str(resp->resp_id), resp->resp_id); return -EINVAL; } @@ -696,8 +700,9 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, err_rb_invalid: dev_err(dev, - "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, resp_sz=%u\n", - stream, rd_ptr, wr_ptr, len, resp_sz); + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, resp_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, wr_ptr, len, + resp_sz); return -EINVAL; } diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 5202232d50c560..48deea79ce6c25 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -7,6 +7,7 @@ #include #include "isp4.h" +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_interface.h" @@ -263,9 +264,9 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return -EINVAL; } - dev_dbg(dev, "channel:%d,fmt %d,w:h=%u:%u,lp:%u,cp%u\n", - cmd_ch_prop.ch, - cmd_ch_prop.image_prop.image_format, + dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n", + isp4dbg_get_out_ch_str(cmd_ch_prop.ch), + isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format), cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, cmd_ch_prop.image_prop.luma_pitch, cmd_ch_prop.image_prop.chroma_pitch); @@ -294,7 +295,8 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return ret; } - dev_dbg(dev, "enable channel %d\n", cmd_ch_en.ch); + dev_dbg(dev, "enable channel %s\n", + isp4dbg_get_out_ch_str(cmd_ch_en.ch)); if (!sensor_info->start_stream_cmd_sent) { ret = isp4sd_kickoff_stream(isp_subdev, @@ -381,8 +383,9 @@ static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); struct device *dev = isp_subdev->dev; - dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n", + dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n", stream_id, + isp4dbg_get_cmd_str(para->cmd_id), para->cmd_id, para->cmd_status, para->cmd_seq_num, ele); @@ -458,8 +461,9 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, return; } - dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%i\n", + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n", ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + isp4dbg_get_buf_done_str(meta->preview.status), meta->preview.status); if (meta->preview.enabled && @@ -468,6 +472,8 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); if (prev) { + isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview, + &prev->buf_info); isp4vid_handle_frame_done(&isp_subdev->isp_vdev, &prev->buf_info); isp4if_dealloc_buffer_node(prev); @@ -475,8 +481,9 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, dev_err(dev, "fail null prev buf\n"); } } else if (meta->preview.enabled) { - dev_err(dev, "fail bad preview status %u\n", - meta->preview.status); + dev_err(dev, "fail bad preview status %u(%s)\n", + meta->preview.status, + isp4dbg_get_buf_done_str(meta->preview.status)); } if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) @@ -493,6 +500,9 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, struct device *dev = isp_subdev->dev; struct isp4fw_resp resp; + if (stream_id == ISP4IF_STREAM_ID_1) + isp_fw_log_print(isp_subdev); + while (true) { if (isp4if_f2h_resp(ispif, stream_id, &resp)) { /* Re-enable the interrupt */ @@ -522,7 +532,8 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, &resp.param.frame_done); break; default: - dev_err(dev, "-><- fail respid (0x%x)\n", + dev_err(dev, "-><- fail respid %s(0x%x)\n", + isp4dbg_get_resp_str(resp.resp_id), resp.resp_id); break; } diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h index ddf6bdf4a62ab1..20ea08a830afd0 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.h +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -109,6 +109,11 @@ struct isp4_subdev { bool irq_enabled; /* spin lock to access ISP_SYS_INT0_EN exclusively */ spinlock_t irq_lock; +#ifdef CONFIG_DEBUG_FS + bool enable_fw_log; + struct dentry *debugfs_dir; + char *fw_log_output; +#endif }; int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, From c3b23294269caeeab89ea287b31f826ee84ce18d Mon Sep 17 00:00:00 2001 From: Bin Du Date: Fri, 20 Mar 2026 16:41:46 +0800 Subject: [PATCH 169/521] FROMLIST: Documentation: add documentation of AMD isp 4 driver Add documentation for AMD ISP 4 and describe the main components Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Link: https://lore.kernel.org/all/20260320084146.200988-1-Bin.Du@amd.com/ Signed-off-by: Mingcong Bai --- Documentation/admin-guide/media/amdisp4-1.rst | 63 +++++++++++++++++++ Documentation/admin-guide/media/amdisp4.dot | 6 ++ .../admin-guide/media/v4l-drivers.rst | 1 + MAINTAINERS | 2 + 4 files changed, 72 insertions(+) create mode 100644 Documentation/admin-guide/media/amdisp4-1.rst create mode 100644 Documentation/admin-guide/media/amdisp4.dot diff --git a/Documentation/admin-guide/media/amdisp4-1.rst b/Documentation/admin-guide/media/amdisp4-1.rst new file mode 100644 index 00000000000000..878141154f962f --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4-1.rst @@ -0,0 +1,63 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +==================================== +AMD Image Signal Processor (amdisp4) +==================================== + +Introduction +============ + +This file documents the driver for the AMD ISP4 that is part of +AMD Ryzen AI Max 300 Series. + +The driver is located under drivers/media/platform/amd/isp4 and uses +the Media-Controller API. + +The driver exposes one video capture device to userspace and provide +web camera like interface. Internally the video device is connected +to the isp4 sub-device responsible for communication with the CCPU FW. + +Topology +======== + +.. _amdisp4_topology_graph: + +.. kernel-figure:: amdisp4.dot + :alt: Diagram of the media pipeline topology + :align: center + + + +The driver has 1 sub-device: Representing isp4 image signal processor. +The driver has 1 video device: Capture device for retrieving images. + +- ISP4 Image Signal Processing Subdevice Node + +--------------------------------------------- + +The isp4 is represented as a single V4L2 subdev, the sub-device does not +provide interface to the user space. The sub-device is connected to one video node +(isp4_capture) with immutable active link. The sub-device represents ISP with +connected sensor similar to smart cameras (sensors with integrated ISP). +sub-device has only one link to the video device for capturing the frames. +The sub-device communicates with CCPU FW for streaming configuration and +buffer management. + + +- isp4_capture - Frames Capture Video Node + +------------------------------------------ + +Isp4_capture is a capture device to capture frames to memory. +The entity is connected to isp4 sub-device. The video device +provides web camera like interface to userspace. It supports +mmap and dma buf types of memory. + +Capturing Video Frames Example +============================== + +.. code-block:: bash + + v4l2-ctl "-d" "/dev/video0" "--set-fmt-video=width=1920,height=1080,pixelformat=NV12" "--stream-mmap" "--stream-count=10" diff --git a/Documentation/admin-guide/media/amdisp4.dot b/Documentation/admin-guide/media/amdisp4.dot new file mode 100644 index 00000000000000..978f30c1a31a4c --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4.dot @@ -0,0 +1,6 @@ +digraph board { + rankdir=TB + n00000001 [label="{{} | amd isp4\n | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port0 -> n00000003 [style=bold] + n00000003 [label="Preview\n/dev/video0", shape=box, style=filled, fillcolor=yellow] +} diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index d31da8e0a54f27..4621eae9fa1edf 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -9,6 +9,7 @@ Video4Linux (V4L) driver-specific documentation .. toctree:: :maxdepth: 2 + amdisp4-1 bttv c3-isp cafe_ccic diff --git a/MAINTAINERS b/MAINTAINERS index 7e148de38296ab..7609c81f7841e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1165,6 +1165,8 @@ M: Nirujogi Pratap L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git +F: Documentation/admin-guide/media/amdisp4-1.rst +F: Documentation/admin-guide/media/amdisp4.dot F: drivers/media/platform/amd/Kconfig F: drivers/media/platform/amd/Makefile F: drivers/media/platform/amd/isp4/Kconfig From eca6f87353befd8cf2a1962301faf9e4ff96fc2c Mon Sep 17 00:00:00 2001 From: Joseph Salisbury Date: Wed, 14 Aug 2013 12:22:49 -0400 Subject: [PATCH 170/521] FROMLIST: intel_ips: blacklist ASUSTek G60JX laptops BugLink: http://bugs.launchpad.net/bugs/1210848 On an ASUSTek G60JX laptop, the intel_ips driver spams the log with a warning message: "ME failed to update for more than 1s, likely hung". This ME doesn't support the feature, so requesting it be blacklisted for now. Signed-off-by: Joseph Salisbury Tested-by: Nick Jenkins Cc: Jesse Barnes Cc: platform-driver-x86@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/d4635f76028172e872cc4be506bad1c11816f75a.1376494861.git.joseph.salisbury@canonical.com/ Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel_ips.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index b1b2d9caba7b38..72700da950b117 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1416,6 +1416,14 @@ static const struct dmi_system_id ips_blacklist[] = { DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook"), }, }, + { + .callback = ips_blacklist_callback, + .ident = "G60JX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G60JX"), + }, + }, { } /* terminating entry */ }; From 352b4c79cf4a1f1b5e2f7cab3eb519278d4e1d7f Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 1 Apr 2026 01:56:57 +0800 Subject: [PATCH 171/521] FROMLIST: PCI: Add per-device flag to disable native PCIe port services Add PCI_DEV_FLAGS_NO_PORT_SERVICES to allow quirks to prevent the PCIe port service driver from probing specific devices. This provides a per-device equivalent of the global pcie_ports=compat kernel parameter. Some platforms have PCIe root ports that break MSI delivery to downstream devices when native port services (AER, PME, bwctrl, etc.) are active. The existing pci_host_bridge native_* flags do not cover all services (notably bwctrl), so a mechanism to skip port driver probing entirely on a per-device basis is needed. Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260331175658.1015829-2-gaohan@iscas.ac.cn Signed-off-by: Han Gao Signed-off-by: Mingcong Bai --- drivers/pci/pcie/portdrv.c | 3 +++ include/linux/pci.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 2d6aa488fe7b64..3386818d200d54 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -685,6 +685,9 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { static int pcie_portdrv_probe(struct pci_dev *dev, const struct pci_device_id *id) { + if (dev->dev_flags & PCI_DEV_FLAGS_NO_PORT_SERVICES) + return -ENODEV; + int type = pci_pcie_type(dev); int status; diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c4454583c115b..10685bc9fd4c47 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -261,6 +261,8 @@ enum pci_dev_flags { * integrated with the downstream devices and doesn't use real PCI. */ PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS = (__force pci_dev_flags_t) (1 << 14), + /* Do not use native PCIe port services (equivalent to pcie_ports=compat) */ + PCI_DEV_FLAGS_NO_PORT_SERVICES = (__force pci_dev_flags_t) (1 << 15), }; enum pci_irq_reroute_variant { From d4ad08e0c56dea7cacdba20db7d19332c6398b48 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 1 Apr 2026 01:56:58 +0800 Subject: [PATCH 172/521] BACKPORT: FROMLIST: PCI: Add quirk to disable PCIe port services on Sophgo SG2042 SG2042's PCIe root ports [1f1c:2042] fail to deliver MSI interrupts to downstream devices when native port services are enabled. Devices under an affected root port receive zero interrupts despite successful vector allocation, causing driver timeouts (e.g. amdgpu fence fallback timer expired on all rings). Set PCI_DEV_FLAGS_NO_PORT_SERVICES on SG2042 root ports to prevent the port service driver from probing, restoring correct MSI delivery. Fixes: 1c72774df028 ("PCI: sg2042: Add Sophgo SG2042 PCIe driver") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260331175658.1015829-3-gaohan@iscas.ac.cn Signed-off-by: Han Gao [ Mingcong Bai: Resolved a minor merge conflict in drivers/pci/quirks.c ] Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 12 ++++++++++++ include/linux/pci_ids.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 0c2580361b3981..fcee338bf0a169 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6389,3 +6389,15 @@ static void pci_release_bar0(struct pci_dev *pdev) DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, 0x4fa0, pci_release_bar0); DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, 0x4fa1, pci_release_bar0); DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, 0xe2ff, pci_release_bar0); + +/* + * SG2042's PCIe root ports do not correctly deliver MSI interrupts to + * downstream devices when native PCIe port services are enabled. All + * services including bwctrl must be disabled, equivalent to pcie_ports=compat. + */ +static void quirk_sg2042_no_port_services(struct pci_dev *dev) +{ + pci_info(dev, "SG2042: disabling native PCIe port services\n"); + dev->dev_flags |= PCI_DEV_FLAGS_NO_PORT_SERVICES; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SOPHGO, 0x2042, quirk_sg2042_no_port_services); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1c9d40e09107dc..1c848bd40de45c 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2632,6 +2632,8 @@ #define PCI_VENDOR_ID_CXL 0x1e98 +#define PCI_VENDOR_ID_SOPHGO 0x1f1c + #define PCI_VENDOR_ID_TEHUTI 0x1fc9 #define PCI_DEVICE_ID_TEHUTI_3009 0x3009 #define PCI_DEVICE_ID_TEHUTI_3010 0x3010 From 54ffaaca13376d7d29e30401b240f470beb26f78 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sun, 21 Dec 2025 16:20:26 +0800 Subject: [PATCH 173/521] FROMLIST: dt-bindings: mmc: spacemit,sdhci: add reset support The SpacemiT SDHCI controller has two reset lines, one connect to AXI bus which shared by all controllers, while another one connect to individual controller separately. Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20251221-07-k1-sdhci-reset-v1-1-6780af7fa6e7@gentoo.org Signed-off-by: Han Gao --- Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml index 34d202af909f2d..ce1edb11893471 100644 --- a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml @@ -62,6 +62,8 @@ required: - interrupts - clocks - clock-names + - resets + - reset-names unevaluatedProperties: false @@ -77,4 +79,6 @@ examples: pinctrl-names = "default", "uhs"; pinctrl-0 = <&sdhci_default_cfg>; pinctrl-1 = <&sdhci_uhs_cfg>; + resets = <&syscon_apmu 2>, <&syscon_apmu 5>; + reset-names = "axi", "sdh"; }; From 99a86d58ee01e6d62da55c8fa349ef3f40bc8718 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Thu, 8 Jan 2026 16:38:54 +0800 Subject: [PATCH 174/521] FROMLIST: regulator: spacemit: MFD_SPACEMIT_P1 as dependencies REGULATOR_SPACEMIT_P1 is a subdevice of P1 and should depend on MFD_SPACEMIT_P1 rather than selecting it directly. Using 'select' does not always respect the parent's dependencies, so 'depends on' is the safer and more correct choice. Since MFD_SPACEMIT_P1 already depends on I2C_K1, the dependency in REGULATOR_SPACEMIT_P1 is now redundant. Additionally, the default value depends on MFD_SPACEMIT_P1 rather than ARCH_SPACEMIT. Acked-by: Mark Brown Acked-by: Alex Elder Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260108-p1-kconfig-fix-v5-1-6fe19f460269@linux.spacemit.com Signed-off-by: Han Gao --- drivers/regulator/Kconfig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 87554ab92801df..c52ff9a3214b7e 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1514,9 +1514,8 @@ config REGULATOR_SLG51000 config REGULATOR_SPACEMIT_P1 tristate "SpacemiT P1 regulators" depends on ARCH_SPACEMIT || COMPILE_TEST - depends on I2C - select MFD_SPACEMIT_P1 - default ARCH_SPACEMIT + depends on MFD_SPACEMIT_P1 + default m if MFD_SPACEMIT_P1 help Enable support for regulators implemented by the SpacemiT P1 power controller. The P1 implements 6 high-efficiency buck From a0bb986bfe3cc72799b82650615471f63737402c Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Thu, 8 Jan 2026 16:38:56 +0800 Subject: [PATCH 175/521] FROMLIST: rtc: spacemit: default module when MFD_SPACEMIT_P1 is enabled The RTC driver defaulted to the same value as MFD_SPACEMIT_P1, which caused it to be built-in automatically whenever the PMIC support was set to y. This is not always desirable, as the RTC function is not required on all platforms using the SpacemiT P1 PMIC. Acked-by: Alex Elder Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260108-p1-kconfig-fix-v5-3-6fe19f460269@linux.spacemit.com Signed-off-by: Han Gao --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 364afc73f8abd2..af420a5f9585a2 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -410,7 +410,7 @@ config RTC_DRV_SPACEMIT_P1 tristate "SpacemiT P1 RTC" depends on ARCH_SPACEMIT || COMPILE_TEST depends on MFD_SPACEMIT_P1 - default MFD_SPACEMIT_P1 + default m if MFD_SPACEMIT_P1 help Enable support for the RTC function in the SpacemiT P1 PMIC. This driver can also be built as a module, which will be called From 863c015281df71df6b0d977af263c547e3ee5869 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 17:24:21 +0800 Subject: [PATCH 176/521] FROMLIST: net: spacemit: Free rings of memory after unmapping DMA In emac_free_{tx,rx}_resources, call dma_free_coherent() to unmap DMA before calling kfree() to deallocate the memory, instead of the other way around. Fixes: bfec6d7f2001 ("net: spacemit: Add K1 Ethernet MAC") Signed-off-by: Vivian Wang Link: https://lore.kernel.org/r/20260303-k1-ethernet-more-fixes-v1-4-0ab0122fdd14@iscas.ac.cn Signed-off-by: Han Gao --- drivers/net/ethernet/spacemit/k1_emac.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/spacemit/k1_emac.c b/drivers/net/ethernet/spacemit/k1_emac.c index f7f16397a2c2ac..02a009afc921a8 100644 --- a/drivers/net/ethernet/spacemit/k1_emac.c +++ b/drivers/net/ethernet/spacemit/k1_emac.c @@ -446,12 +446,12 @@ static void emac_free_tx_resources(struct emac_priv *priv) emac_clean_tx_desc_ring(priv); - kfree(tr->tx_desc_buf); - tr->tx_desc_buf = NULL; - dma_free_coherent(dev, tr->total_size, tr->desc_addr, tr->desc_dma_addr); tr->desc_addr = NULL; + + kfree(tr->tx_desc_buf); + tr->tx_desc_buf = NULL; } static void emac_free_rx_resources(struct emac_priv *priv) @@ -461,12 +461,12 @@ static void emac_free_rx_resources(struct emac_priv *priv) emac_clean_rx_desc_ring(priv); - kfree(rr->rx_desc_buf); - rr->rx_desc_buf = NULL; - dma_free_coherent(dev, rr->total_size, rr->desc_addr, rr->desc_dma_addr); rr->desc_addr = NULL; + + kfree(rr->rx_desc_buf); + rr->rx_desc_buf = NULL; } static int emac_tx_clean_desc(struct emac_priv *priv) From e6b7a7d9706b0ddd721b6f4120cea21318f7aec9 Mon Sep 17 00:00:00 2001 From: Shuwei Wu Date: Fri, 26 Jun 2026 16:10:23 +0800 Subject: [PATCH 177/521] FROMLIST: cpufreq: dt-platdev: Add SpacemiT K1 SoC to the allowlist Add the compatible string for supporting the generic cpufreq driver on the SpacemiT K1 SoC. Signed-off-by: Shuwei Wu Tested-by: Gong Shuai # OrangePi-RV2 Link: https://lore.kernel.org/r/20260626-shadow-deps-v4-1-bba9831f2f1d@mailbox.org Signed-off-by: Han Gao --- drivers/cpufreq/cpufreq-dt-platdev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index ff1204c666b197..26f81f4d56df85 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -81,6 +81,8 @@ static const struct of_device_id allowlist[] __initconst = { { .have_governor_per_policy = true, }, }, + { .compatible = "spacemit,k1", }, + { .compatible = "st-ericsson,u8500", }, { .compatible = "st-ericsson,u8540", }, { .compatible = "st-ericsson,u9500", }, From 736e77d98b559301ff9504b1b65460ecf919911b Mon Sep 17 00:00:00 2001 From: Shuwei Wu Date: Fri, 26 Jun 2026 16:10:24 +0800 Subject: [PATCH 178/521] FROMLIST: riscv: dts: spacemit: Add cpu scaling for K1 SoC Add CPU clock properties and OPP tables for the two CPU clusters in the SpacemiT K1 SoC. The OPP entries use voltage ranges because the CPU supply is shared by both clusters. Enable CPU DVFS on Banana Pi BPI-F3 by including the OPP tables and wiring the CPU nodes to the CPU regulator supply. Signed-off-by: Shuwei Wu Tested-by: Andre Heider # k1-musepi-pro Tested-by: Gong Shuai # OrangePi-RV2 Link: https://lore.kernel.org/r/20260626-shadow-deps-v4-2-bba9831f2f1d@mailbox.org Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-bananapi-f3.dts | 35 +++++- arch/riscv/boot/dts/spacemit/k1-opp.dtsi | 105 ++++++++++++++++++ arch/riscv/boot/dts/spacemit/k1.dtsi | 8 ++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/boot/dts/spacemit/k1-opp.dtsi diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 72f77e9edd63db..307a7e4a894347 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -4,6 +4,7 @@ */ #include "k1.dtsi" +#include "k1-opp.dtsi" #include "k1-pinctrl.dtsi" / { @@ -87,6 +88,38 @@ status = "okay"; }; +&cpu_0 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_1 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_2 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_3 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_4 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_5 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_6 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_7 { + cpu-supply = <&buck1_0v9>; +}; + &emmc { bus-width = <8>; mmc-hs400-1_8v; @@ -202,7 +235,7 @@ dldoin2-supply = <&buck5>; regulators { - buck1 { + buck1_0v9: buck1 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3450000>; regulator-ramp-delay = <5000>; diff --git a/arch/riscv/boot/dts/spacemit/k1-opp.dtsi b/arch/riscv/boot/dts/spacemit/k1-opp.dtsi new file mode 100644 index 00000000000000..4cebfcd87485f2 --- /dev/null +++ b/arch/riscv/boot/dts/spacemit/k1-opp.dtsi @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +/ { + cluster0_opp_table: opp-table-cluster0 { + compatible = "operating-points-v2"; + opp-shared; + + opp-614400000 { + opp-hz = /bits/ 64 <614400000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-819000000 { + opp-hz = /bits/ 64 <819000000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-1228800000 { + opp-hz = /bits/ 64 <1228800000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-1600000000 { + opp-hz = /bits/ 64 <1600000000>; + opp-microvolt = <1050000 1050000 1050000>; + clock-latency-ns = <200000>; + }; + }; + + cluster1_opp_table: opp-table-cluster1 { + compatible = "operating-points-v2"; + opp-shared; + + opp-614400000 { + opp-hz = /bits/ 64 <614400000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-819000000 { + opp-hz = /bits/ 64 <819000000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-1228800000 { + opp-hz = /bits/ 64 <1228800000>; + opp-microvolt = <950000 950000 1050000>; + clock-latency-ns = <200000>; + }; + + opp-1600000000 { + opp-hz = /bits/ 64 <1600000000>; + opp-microvolt = <1050000 1050000 1050000>; + clock-latency-ns = <200000>; + }; + }; +}; + +&cpu_0 { + operating-points-v2 = <&cluster0_opp_table>; +}; + +&cpu_1 { + operating-points-v2 = <&cluster0_opp_table>; +}; + +&cpu_2 { + operating-points-v2 = <&cluster0_opp_table>; +}; + +&cpu_3 { + operating-points-v2 = <&cluster0_opp_table>; +}; + +&cpu_4 { + operating-points-v2 = <&cluster1_opp_table>; +}; + +&cpu_5 { + operating-points-v2 = <&cluster1_opp_table>; +}; + +&cpu_6 { + operating-points-v2 = <&cluster1_opp_table>; +}; + +&cpu_7 { + operating-points-v2 = <&cluster1_opp_table>; +}; diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index 08a0f28d011fea..4a359190f63350 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -54,6 +54,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <0>; + clocks = <&syscon_apmu CLK_CPU_C0_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", @@ -84,6 +85,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <1>; + clocks = <&syscon_apmu CLK_CPU_C0_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", @@ -114,6 +116,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <2>; + clocks = <&syscon_apmu CLK_CPU_C0_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", @@ -144,6 +147,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <3>; + clocks = <&syscon_apmu CLK_CPU_C0_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", @@ -174,6 +178,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <4>; + clocks = <&syscon_apmu CLK_CPU_C1_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", @@ -204,6 +209,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <5>; + clocks = <&syscon_apmu CLK_CPU_C1_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", @@ -234,6 +240,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <6>; + clocks = <&syscon_apmu CLK_CPU_C1_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", @@ -264,6 +271,7 @@ compatible = "spacemit,x60", "riscv"; device_type = "cpu"; reg = <7>; + clocks = <&syscon_apmu CLK_CPU_C1_CORE>; riscv,isa = "rv64imafdcbv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt"; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "b", "v", "zicbom", From 49457c8145c2db4798f50ce9dccb6937468606c9 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Tue, 17 Mar 2026 16:48:06 +0800 Subject: [PATCH 179/521] FROMLIST: riscv: disable local interrupts and stop other CPUs before reboot/shutdown Currently, the RISC-V implementation of machine_restart(), machine_halt(), and machine_power_off() invokes the kernel teardown chains (e.g., do_kernel_restart()) with local interrupts enabled and other CPUs still running. This implementation fails to provide a deterministic execution environment for registered handlers in the restart or power-off notifier chains. These chains are intended to be executed in a strict atomic and single-threaded context. Specifically, under CONFIG_PREEMPT_RCU, rcu_read_lock() does not increment the preempt_count. If local interrupts remain enabled, the environment is not guaranteed to be atomic. This can lead to a context misidentification within generic kernel teardown code, causing it to incorrectly enter non-atomic paths (such as attempting to acquire sleeping locks), which results in fatal "scheduling while atomic" splats or system hangs. Additionally, stopping other CPUs ensures the primary CPU has exclusive access to the hardware state during the final teardown phase, preventing unpredictable interference from other active cores. Align RISC-V with other major architectures by disabling local interrupts and stopping other CPUs at the beginning of the shutdown sequences. This guarantees the architectural expectations of the kernel's restart and power-off handlers are met. Signed-off-by: Troy Mitchell Tested-by: Aurelien Jarno Link: https://lore.kernel.org/all/abEP80FoCn4S4-WG@aurel32.net/ [1] Link: https://lore.kernel.org/r/20260317-v7-0-rc1-rv-dis-int-before-restart-v2-1-0ecc85fbb7ff@linux.dev Signed-off-by: Han Gao --- arch/riscv/kernel/reset.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/kernel/reset.c b/arch/riscv/kernel/reset.c index 9122885722265c..8c48466c50e9a0 100644 --- a/arch/riscv/kernel/reset.c +++ b/arch/riscv/kernel/reset.c @@ -5,6 +5,7 @@ #include #include +#include static void default_power_off(void) { @@ -17,18 +18,27 @@ EXPORT_SYMBOL(pm_power_off); void machine_restart(char *cmd) { + local_irq_disable(); + smp_send_stop(); + do_kernel_restart(cmd); while (1); } void machine_halt(void) { + local_irq_disable(); + smp_send_stop(); + do_kernel_power_off(); default_power_off(); } void machine_power_off(void) { + local_irq_disable(); + smp_send_stop(); + do_kernel_power_off(); default_power_off(); } From 9b8079471a8bb3638a0e2b3716112e37b70f23a0 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 21 Mar 2026 03:12:10 +0800 Subject: [PATCH 180/521] FROMLIST: drm/bridge: th1520-dw-hdmi: Fix error check on dw_hdmi_probe() return value The error check after calling dw_hdmi_probe() was incorrectly checking the struct pointer hdmi instead of the probe result hdmi->dw_hdmi. Fix this by checking the correct variable. Fixes: 96f30ee0fb9d ("drm/bridge: add a driver for T-Head TH1520 HDMI controller") Signed-off-by: Felix Gu Reviewed-by: Dmitry Baryshkov Reviewed-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260321-th1520-v1-1-ec877197770d@gmail.com Signed-off-by: Han Gao --- drivers/gpu/drm/bridge/th1520-dw-hdmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/th1520-dw-hdmi.c b/drivers/gpu/drm/bridge/th1520-dw-hdmi.c index 389eead5f1c45f..c9968ec1823c16 100644 --- a/drivers/gpu/drm/bridge/th1520-dw-hdmi.c +++ b/drivers/gpu/drm/bridge/th1520-dw-hdmi.c @@ -136,8 +136,8 @@ static int th1520_dw_hdmi_probe(struct platform_device *pdev) plat_data->priv_data = hdmi; hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data); - if (IS_ERR(hdmi)) - return PTR_ERR(hdmi); + if (IS_ERR(hdmi->dw_hdmi)) + return PTR_ERR(hdmi->dw_hdmi); platform_set_drvdata(pdev, hdmi); From 94ea69303ace209c887102cf9f67ffcc7daaf019 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 21 Mar 2026 03:12:11 +0800 Subject: [PATCH 181/521] FROMLIST: drm/bridge: th1520-dw-hdmi: Fix remove() callback This driver stores struct th1520_hdmi * in platform drvdata, but th1520_dw_hdmi_remove() was reading it back as struct dw_hdmi * and passing it to dw_hdmi_remove(), so teardown runs on the wrong pointer. Retrieve struct th1520_hdmi * from platform drvdata and pass hdmi->dw_hdmi to dw_hdmi_remove(). Fixes: 96f30ee0fb9d ("drm/bridge: add a driver for T-Head TH1520 HDMI controller") Signed-off-by: Felix Gu Reviewed-by: Dmitry Baryshkov Reviewed-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260321-th1520-v1-2-ec877197770d@gmail.com Signed-off-by: Han Gao --- drivers/gpu/drm/bridge/th1520-dw-hdmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/th1520-dw-hdmi.c b/drivers/gpu/drm/bridge/th1520-dw-hdmi.c index c9968ec1823c16..6ec9003a8f3f7d 100644 --- a/drivers/gpu/drm/bridge/th1520-dw-hdmi.c +++ b/drivers/gpu/drm/bridge/th1520-dw-hdmi.c @@ -146,9 +146,9 @@ static int th1520_dw_hdmi_probe(struct platform_device *pdev) static void th1520_dw_hdmi_remove(struct platform_device *pdev) { - struct dw_hdmi *hdmi = platform_get_drvdata(pdev); + struct th1520_hdmi *hdmi = platform_get_drvdata(pdev); - dw_hdmi_remove(hdmi); + dw_hdmi_remove(hdmi->dw_hdmi); } static const struct of_device_id th1520_dw_hdmi_of_table[] = { From a68efc2fc4adc508dac2b763c43d14066bff5326 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Tue, 31 Mar 2026 15:37:22 +0800 Subject: [PATCH 182/521] FROMLIST: riscv: mm: fix SWIOTLB initialization for systems with DRAM above 4GB On RISC-V platforms where the entire physical memory (DRAM) resides above the 32-bit address space (i.e., above dma32_phys_limit), the current SWIOTLB initialization logic fails. This patch addresses two interconnected issues on such platforms: 1. Incorrect 32-bit DMA bounce assumption: The existing condition `max_pfn > PFN_DOWN(dma32_phys_limit)` assumes that a 32-bit DMA bounce buffer is required simply because the maximum PFN exceeds the 32-bit limit. However, if all DRAM starts above 4GB, no memory exists below the limit to satisfy this allocation. Fix this by adding a check to ensure `memblock_start_of_DRAM()` is actually below the 32-bit limit before enforcing 32-bit SWIOTLB. 2. kmalloc() bounce buffer allocation failure on non-coherent systems: For non-coherent hardware, a bounce buffer is still mandatory for cache-line-aligned kmalloc(), even if 32-bit DMA bouncing is skipped. Without the `SWIOTLB_ANY` flag, swiotlb_init() defaults to allocating from low memory, which fails completely when DRAM only exists in high memory. By appending `SWIOTLB_ANY` to swiotlb_flags, the allocator is permitted to allocate this alignment buffer from high memory. With this patch, systems with non-coherent DMA and DRAM entirely above 4GB can successfully map the software IO TLB in high memory and boot normally. Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260331-fix-riscv-swiotlb-v1-1-74dd5e6be0f1@linux.dev Signed-off-by: Han Gao --- arch/riscv/mm/init.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 06596010418a19..5f45df119200ad 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -164,7 +164,9 @@ static void print_vm_layout(void) { } void __init arch_mm_preinit(void) { - bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit); + bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit) && + memblock_start_of_DRAM() < dma32_phys_limit; + unsigned int swiotlb_flags = SWIOTLB_VERBOSE; #ifdef CONFIG_FLATMEM BUG_ON(!mem_map); #endif /* CONFIG_FLATMEM */ @@ -172,17 +174,21 @@ void __init arch_mm_preinit(void) if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb && dma_cache_alignment != 1) { /* - * If no bouncing needed for ZONE_DMA, allocate 1MB swiotlb - * buffer per 1GB of RAM for kmalloc() bouncing on - * non-coherent platforms. + * No 32-bit DMA bouncing needed (either all DRAM is within + * the 32-bit limit, or it all starts above it), but + * non-coherent hardware still requires cache-line-aligned + * bounce buffers for kmalloc(). Use SWIOTLB_ANY so that the + * buffer can be allocated from high memory when DRAM starts + * above dma32_phys_limit. Allocate ~1 MB per 1 GB of RAM. */ unsigned long size = DIV_ROUND_UP(memblock_phys_mem_size(), 1024); swiotlb_adjust_size(min(swiotlb_size_or_default(), size)); swiotlb = true; + swiotlb_flags |= SWIOTLB_ANY; } - swiotlb_init(swiotlb, SWIOTLB_VERBOSE); + swiotlb_init(swiotlb, swiotlb_flags); print_vm_layout(); } From 5310159384c5351757b56e17d7e26a3fe9e5426c Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Wed, 25 Mar 2026 13:46:08 +0530 Subject: [PATCH 183/521] FROMLIST: riscv: dts: spacemit: k1-bananapi-f3: Add vcc5v0_sys regulator for Banana Pi F3 Define the system 5V fixed regulator (vcc5v0_sys) supplied by the DC input. As per the schematics, vcc5v0_sys is the input power source for the VCC5V0_HUB and 5V_VBUS reglators. Update these regulators to correctly reference vcc5v0_sys as their parent (vin-supply). Cc: Han Gao Cc: Ze Huang Cc: Chukun Pan Signed-off-by: Anand Moon Link: https://lore.kernel.org/r/20260325081700.1502-2-linux.amoon@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 307a7e4a894347..fbb4ff5e169fdb 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -54,6 +54,16 @@ regulator-always-on; }; + reg_vcc5v0_sys: regulator-vcc5v0-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + vin-supply = <®_dc_in>; + }; + reg_vcc_4v: regulator-vcc-4v { compatible = "regulator-fixed"; regulator-name = "vcc_4v"; @@ -70,6 +80,7 @@ regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; regulator-always-on; + vin-supply = <®_vcc5v0_sys>; gpio = <&gpio K1_GPIO(97) GPIO_ACTIVE_HIGH>; enable-active-high; }; @@ -79,6 +90,7 @@ regulator-name = "USB30_HUB"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; + vin-supply = <®_vcc5v0_sys>; gpio = <&gpio K1_GPIO(123) GPIO_ACTIVE_HIGH>; enable-active-high; }; From 609a3bcb07b2766dad3c8c2a1c9966c4d80d73fb Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Wed, 25 Mar 2026 13:46:09 +0530 Subject: [PATCH 184/521] FROMLIST: riscv: dts: spacemit: k1-bananapi-f3: Update USB regulator on onboard usb and lable Update the USB regulator labels to align with the board schematics and power hierarchy. This change renames the regulator to reg_5v_vbus and its name to 5V_VBUS. Additionally, it fixes the vdd-supply references for both the USB 2.0 and 3.0 hub nodes to ensure they correctly point to the 5V_VBUS input source as per the board schematics. Cc: Han Gao Cc: Ze Huang Cc: Chukun Pan Signed-off-by: Anand Moon Link: https://lore.kernel.org/r/20260325081700.1502-3-linux.amoon@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index fbb4ff5e169fdb..e97519a9a55ee5 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -74,9 +74,9 @@ vin-supply = <®_dc_in>; }; - regulator-usb3-vbus-5v { + reg_5v_vbus: regulator-usb3-vbus-5v { compatible = "regulator-fixed"; - regulator-name = "USB30_VBUS"; + regulator-name = "5V_VBUS"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; regulator-always-on; @@ -408,7 +408,7 @@ hub_2_0: hub@1 { compatible = "usb2109,2817"; reg = <0x1>; - vdd-supply = <&usb3_hub_5v>; + vdd-supply = <®_5v_vbus>; peer-hub = <&hub_3_0>; reset-gpios = <&gpio K1_GPIO(124) GPIO_ACTIVE_LOW>; }; @@ -416,7 +416,7 @@ hub_3_0: hub@2 { compatible = "usb2109,817"; reg = <0x2>; - vdd-supply = <&usb3_hub_5v>; + vdd-supply = <®_5v_vbus>; peer-hub = <&hub_2_0>; reset-gpios = <&gpio K1_GPIO(124) GPIO_ACTIVE_LOW>; }; From f807270e35abf54462498bfcaf85dcf823a64dcc Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Wed, 25 Mar 2026 13:46:10 +0530 Subject: [PATCH 185/521] FROMLIST: riscv: dts: spacemit: k1-bananapi-f3: Correct USB hub power hierarchy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link the usb3_hub_5v regulator to the USB controller’s vbus-supply to ensure the USB 3.0 stack is properly powered as per the schematics. In addition, align the USB hub regulator with the board schematics by renaming it to VCC5V0_HUB and marking it as regulator-always-on to maintain power stability. Cc: Han Gao Cc: Ze Huang Cc: Chukun Pan Signed-off-by: Anand Moon Link: https://lore.kernel.org/r/20260325081700.1502-4-linux.amoon@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index e97519a9a55ee5..8d2c161f5b356b 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -87,9 +87,10 @@ usb3_hub_5v: regulator-usb3-hub-5v { compatible = "regulator-fixed"; - regulator-name = "USB30_HUB"; + regulator-name = "VCC5V0_HUB"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; + regulator-always-on; vin-supply = <®_vcc5v0_sys>; gpio = <&gpio K1_GPIO(123) GPIO_ACTIVE_HIGH>; enable-active-high; @@ -403,6 +404,7 @@ dr_mode = "host"; #address-cells = <1>; #size-cells = <0>; + vbus-supply = <&usb3_hub_5v>; status = "okay"; hub_2_0: hub@1 { From 3f9f537e944bd9217d10c95d054af244ad03351b Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Fri, 24 Apr 2026 11:03:26 +0800 Subject: [PATCH 186/521] FROMLIST: riscv: mm: Do not probe satp mode limit if known in FDT Some systems may only have memory at such a high address that an identity mapping cannot be made in Sv48, or even Sv57. To avoid crashing while probing satp mode support, trust the mmu-type property from FDT and avoid probing if it is set. For example, if a CPU supports (up to) Sv57 but the SoC it is on only has memory starting at 128 TiB, an identity mapping cannot be made in Sv48. If the user specifies "no5lvl" on the command line, set_satp_mode() will attempt to test Sv48 support and crash, since the virtual address is too high. Instead, given the correct FDT CPU node property mmu-type = "riscv,sv57", set_satp_mode() can pick the maximum allowed mode from command line and FDT, namely Sv48, without probing. Handling of mmu-type "riscv,sv57" in set_satp_mode_from_fdt() is added since it is now needed. Signed-off-by: Vivian Wang Link: https://lore.kernel.org/r/20260424-riscv-mm-trust-fdt-mmu-type-v1-1-57f3313275d2@iscas.ac.cn Signed-off-by: Han Gao --- arch/riscv/kernel/pi/fdt_early.c | 2 ++ arch/riscv/mm/init.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/pi/fdt_early.c b/arch/riscv/kernel/pi/fdt_early.c index a12ff8090f1903..7022532875604d 100644 --- a/arch/riscv/kernel/pi/fdt_early.c +++ b/arch/riscv/kernel/pi/fdt_early.c @@ -218,6 +218,8 @@ u64 set_satp_mode_from_fdt(uintptr_t dtb_pa) return SATP_MODE_39; else if (!strcmp(mmu_type, "riscv,sv48")) return SATP_MODE_48; + else if (!strcmp(mmu_type, "riscv,sv57")) + return SATP_MODE_57; break; } diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 5f45df119200ad..2ec9a9df20cb87 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -834,8 +834,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa) { u64 identity_satp, hw_satp; uintptr_t set_satp_mode_pmd = ((unsigned long)set_satp_mode) & PMD_MASK; + u64 satp_mode_limit_fdt = __pi_set_satp_mode_from_fdt(dtb_pa); u64 satp_mode_limit = min_not_zero(__pi_set_satp_mode_from_cmdline(dtb_pa), - __pi_set_satp_mode_from_fdt(dtb_pa)); + satp_mode_limit_fdt); kernel_map.page_offset = PAGE_OFFSET_L5; @@ -847,6 +848,10 @@ static __init void set_satp_mode(uintptr_t dtb_pa) return; } + /* Skip probing if max SATP mode known from FDT */ + if (satp_mode_limit_fdt) + return; + create_p4d_mapping(early_p4d, set_satp_mode_pmd, (uintptr_t)early_pud, P4D_SIZE, PAGE_TABLE); From f1420af18db8a98dd2dcfe0b576d628bf0d1489c Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Thu, 7 May 2026 18:31:44 +0800 Subject: [PATCH 187/521] FROMLIST: ASoC: spacemit: declare shared BCLK for cross-DAI rate constraint On SpacemiT K3, multiple I2S controllers share the same physical BCLK (c_bclk). Declare this clock via snd_soc_dai_set_bclk_clk() so the ASoC core can automatically constrain hw_params when one controller is already streaming. For K1, c_bclk is NULL (obtained via devm_clk_get_optional_enabled), so this call is a no-op and behavior is unchanged. Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260507-i2s-same-blk-v2-3-ede05a22f732@linux.spacemit.com Signed-off-by: Han Gao --- sound/soc/spacemit/k1_i2s.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index 8871fc15b29cc0..d35b57566829a0 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -323,6 +323,8 @@ static int spacemit_i2s_dai_probe(struct snd_soc_dai *dai) spacemit_i2s_init(i2s); + snd_soc_dai_set_bclk_clk(dai, i2s->c_bclk); + return 0; } From 12449cb72fcad51d6a187c487d7790cbeb83566a Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:16:59 +0800 Subject: [PATCH 188/521] FROMLIST: dt-bindings: clock: thead: add TH1520 MISC subsys clock controller TH1520 has a subsystem clock controller called MISC_SUBSYS in its manual, mainly controlling clocks for USB and MMC/SD in non-TEE environment. Add device tree binding for it. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-2-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- .../devicetree/bindings/clock/thead,th1520-clk-ap.yaml | 5 +++-- include/dt-bindings/clock/thead,th1520-clk-ap.h | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml b/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml index 9d058c00ab3d59..d46d13597466fe 100644 --- a/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml +++ b/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml @@ -23,6 +23,7 @@ properties: compatible: enum: - thead,th1520-clk-ap + - thead,th1520-clk-misc - thead,th1520-clk-vo reg: @@ -32,8 +33,8 @@ properties: items: - description: | One input clock: - - For "thead,th1520-clk-ap": the clock input must be the 24 MHz - main oscillator. + - For "thead,th1520-clk-ap" and "thead,th1520-clk-misc": the clock + input must be the 24 MHz main oscillator. - For "thead,th1520-clk-vo": the clock input must be the VIDEO_PLL, which is configured by the AP clock controller. According to the TH1520 manual, VIDEO_PLL is a Silicon Creations Sigma-Delta PLL diff --git a/include/dt-bindings/clock/thead,th1520-clk-ap.h b/include/dt-bindings/clock/thead,th1520-clk-ap.h index 68b35cc6120413..642c2a69a57971 100644 --- a/include/dt-bindings/clock/thead,th1520-clk-ap.h +++ b/include/dt-bindings/clock/thead,th1520-clk-ap.h @@ -128,4 +128,14 @@ #define CLK_MIPIDSI1_PIXCLK 29 #define CLK_HDMI_PIXCLK 30 +/* MISC clocks */ +#define CLK_MISCSYS_ACLK 0 +#define CLK_USB 1 +#define CLK_USB_CTL_REF 2 +#define CLK_USB_PHY_REF 3 +#define CLK_USB_SUSPEND 4 +#define CLK_EMMC 5 +#define CLK_SDIO0 6 +#define CLK_SDIO1 7 + #endif From 747a6c81f8a5e80ef1e05e93442dd0cb3115fa5d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:00 +0800 Subject: [PATCH 189/521] FROMLIST: clk: thead: th1520-ap: add support for MISC subsys clocks The TH1520 SoC contains a MISC_SUBSYS clock controller, which allows controlling of USB related clocks and MMC/SD controller AHB bus clocks. Add support for this clock controller, in order to enable USB support. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-3-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- drivers/clk/thead/clk-th1520-ap.c | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c index 3a6847f1c950fc..24f785f0b329a7 100644 --- a/drivers/clk/thead/clk-th1520-ap.c +++ b/drivers/clk/thead/clk-th1520-ap.c @@ -1266,6 +1266,41 @@ static CCU_GATE(CLK_MIPIDSI1_PIXCLK, mipi_dsi1_pixclk, "mipi-dsi1-pixclk", static CCU_GATE(CLK_HDMI_PIXCLK, hdmi_pixclk, "hdmi-pixclk", video_pll_clk_pd, 0x4, 0, 0); +static struct clk_fixed_factor usb_suspend_div_clk = { + .div = 24, + .mult = 1, + .hw.init = CLK_HW_INIT_PARENTS_DATA("usb-suspend-div", + osc_24m_clk, + &clk_fixed_factor_ops, + 0), +}; + +static const struct clk_parent_data usb_suspend_parents[] = { + { .hw = &usb_suspend_div_clk.hw }, +}; + +static CCU_GATE(CLK_MISCSYS_ACLK, miscsys_aclk, "miscsys-aclk", axi_aclk_pd, + 0x0, 0, CLK_IS_CRITICAL); + +static const struct clk_parent_data miscsys_aclk_pd[] = { + { .hw = &miscsys_aclk.gate.hw }, +}; + +static CCU_GATE(CLK_USB, usb_clk, "usb", miscsys_aclk_pd, 0x4, 0, + CLK_IS_CRITICAL); +static CCU_GATE(CLK_USB_CTL_REF, usb_ctl_ref_clk, "usb-ctl-ref", osc_24m_clk, + 0x4, 1, 0); +static CCU_GATE(CLK_USB_PHY_REF, usb_phy_ref_clk, "usb-phy-ref", osc_24m_clk, + 0x4, 2, 0); +static CCU_GATE(CLK_USB_SUSPEND, usb_suspend_clk, "usb-suspend", + usb_suspend_parents, 0x4, 3, 0); +static CCU_GATE(CLK_EMMC, emmc_clk, "emmc", perisys_ahb_hclk_pd, 0x8, 0, + 0); +static CCU_GATE(CLK_SDIO0, sdio0_clk, "sdio0", perisys_ahb_hclk_pd, 0xc, 0, + 0); +static CCU_GATE(CLK_SDIO1, sdio1_clk, "sdio1", perisys_ahb_hclk_pd, 0x10, 0, + 0); + static CLK_FIXED_FACTOR_HW(gmac_pll_clk_100m, "gmac-pll-clk-100m", &gmac_pll_clk.common.hw, 10, 1, 0); @@ -1410,6 +1445,17 @@ static struct ccu_gate *th1520_vo_gate_clks[] = { &hdmi_pixclk }; +static struct ccu_gate *th1520_misc_gate_clks[] = { + &miscsys_aclk, + &usb_clk, + &usb_ctl_ref_clk, + &usb_phy_ref_clk, + &usb_suspend_clk, + &emmc_clk, + &sdio0_clk, + &sdio1_clk +}; + static const struct regmap_config th1520_clk_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -1451,6 +1497,14 @@ static const struct th1520_plat_data th1520_vo_platdata = { .nr_gate_clks = ARRAY_SIZE(th1520_vo_gate_clks), }; +static const struct th1520_plat_data th1520_misc_platdata = { + .th1520_gate_clks = th1520_misc_gate_clks, + + .nr_clks = CLK_SDIO1 + 1, + + .nr_gate_clks = ARRAY_SIZE(th1520_misc_gate_clks), +}; + /* * Maintain clock rate of c910_bus_clk below TH1520_C910_BUS_MAX_RATE (750MHz) * when its parent, c910_clk, changes the rate. @@ -1609,6 +1663,12 @@ static int th1520_clk_probe(struct platform_device *pdev) return ret; } + if (plat_data == &th1520_ap_platdata) { + ret = devm_clk_hw_register(dev, &usb_suspend_div_clk.hw); + if (ret) + return ret; + } + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv); if (ret) return ret; @@ -1625,6 +1685,10 @@ static const struct of_device_id th1520_clk_match[] = { .compatible = "thead,th1520-clk-vo", .data = &th1520_vo_platdata, }, + { + .compatible = "thead,th1520-clk-misc", + .data = &th1520_misc_platdata, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, th1520_clk_match); From e5167034a893c591717a8b6f35aa0d5eb5fbe8e5 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:01 +0800 Subject: [PATCH 190/521] FROMLIST: riscv: dts: thead: add device tree node for MISC clock controller The MISC_SUBSYS clock controller on TH1520 SoC is a clock controller mainly controlling USB-related clocks (which isn't utilized yet) and MMC/SD controllers' AHB bus clocks. Add the device tree node for it along with the missing bus clock references for MMC/SD controllers. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-4-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- arch/riscv/boot/dts/thead/th1520.dtsi | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index f2abc6338458ba..f543298614960d 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -401,8 +401,8 @@ compatible = "thead,th1520-dwcmshc"; reg = <0xff 0xe7080000 0x0 0x10000>; interrupts = <62 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk CLK_EMMC_SDIO>; - clock-names = "core"; + clocks = <&clk CLK_EMMC_SDIO>, <&clk_misc CLK_EMMC>; + clock-names = "core", "bus"; status = "disabled"; }; @@ -410,8 +410,8 @@ compatible = "thead,th1520-dwcmshc"; reg = <0xff 0xe7090000 0x0 0x10000>; interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk CLK_EMMC_SDIO>; - clock-names = "core"; + clocks = <&clk CLK_EMMC_SDIO>, <&clk_misc CLK_SDIO0>; + clock-names = "core", "bus"; status = "disabled"; }; @@ -419,8 +419,8 @@ compatible = "thead,th1520-dwcmshc"; reg = <0xff 0xe70a0000 0x0 0x10000>; interrupts = <71 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk CLK_EMMC_SDIO>; - clock-names = "core"; + clocks = <&clk CLK_EMMC_SDIO>, <&clk_misc CLK_SDIO1>; + clock-names = "core", "bus"; status = "disabled"; }; @@ -579,6 +579,13 @@ #reset-cells = <1>; }; + clk_misc: clock-controller@ffec02c100 { + compatible = "thead,th1520-clk-misc"; + reg = <0xff 0xec02c100 0x0 0x100>; + clocks = <&osc>; + #clock-cells = <1>; + }; + rst_vp: reset-controller@ffecc30000 { compatible = "thead,th1520-reset-vp"; reg = <0xff 0xecc30000 0x0 0x14>; From d595cf8b75876d1351e108afa35fb9c866485819 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:02 +0800 Subject: [PATCH 191/521] FROMLIST: dt-bindings: phy: add binding for T-Head TH1520 USB PHY The TH1520 SoC features a Synopsys USB 3.0 FemtoPHY with some custom glue logic configuring PHY parameters. Add a binding for it. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-5-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- .../bindings/phy/thead,th1520-usb-phy.yaml | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/thead,th1520-usb-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/thead,th1520-usb-phy.yaml b/Documentation/devicetree/bindings/phy/thead,th1520-usb-phy.yaml new file mode 100644 index 00000000000000..37f5cfb95bad01 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/thead,th1520-usb-phy.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/thead,th1520-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-Head TH1520 USB PHY + +description: | + The T-HEAD TH1520 USB PHY is a Synopsys USB 3.0 FemtoPHY glued with some + custom logic to configure PHY parameters. + +maintainers: + - Icenowy Zheng + - Wei Fu + - Drew Fustini + +properties: + compatible: + const: thead,th1520-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: PHY bus clock + - description: PHY reference clock + + clock-names: + items: + - const: bus + - const: ref + + resets: + items: + - description: PHY bus reset + - description: PHY reset + + reset-names: + items: + - const: bus + - const: phy + + avdd33-usb3-supply: + description: | + 3.3V power supply for the PHY, named AVDD33_USB3 in the SoC pin list. + +required: + - compatible + - "#phy-cells" + - clocks + - clock-names + - resets + - reset-names + - avdd33-usb3-supply + +additionalProperties: false + +examples: + - | + phy@ec030000 { + compatible = "thead,th1520-usb-phy"; + reg = <0xec030000 0x10000>; + #phy-cells = <0>; + clocks = <&clk_misc 1>, <&clk_misc 3>; + clock-names = "bus", "ref"; + resets = <&rst_misc 6>, <&rst_misc 7>; + reset-names = "bus", "phy"; + avdd33-usb3-supply = <&avdd33_usb3>; + }; From ab0a71f62b2a73eec2f4ed075e68ab10e53bbc93 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:03 +0800 Subject: [PATCH 192/521] FROMLIST: phy: add a driver for T-Head TH1520 USB PHY The USB PHY on T-Head TH1520 SoC is a Synopsys USB 3.0 FemtoPHY, with some PHY parameters exported as another system controller along with it. As a few PHY parameters' default value isn't ready to work, add a driver configuring them before letting the PHY run, in addition to clock/reset/regulator management. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-6-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/thead/Kconfig | 12 ++ drivers/phy/thead/Makefile | 2 + drivers/phy/thead/phy-th1520-usb.c | 197 +++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+) create mode 100644 drivers/phy/thead/Kconfig create mode 100644 drivers/phy/thead/Makefile create mode 100644 drivers/phy/thead/phy-th1520-usb.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 227b9a4c612e8c..ea1a52e14b8397 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -164,6 +164,7 @@ source "drivers/phy/st/Kconfig" source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" +source "drivers/phy/thead/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/xilinx/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f49d83f00a3d87..4604522548c913 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -48,5 +48,6 @@ obj-$(CONFIG_GENERIC_PHY) += allwinner/ \ starfive/ \ sunplus/ \ tegra/ \ + thead/ \ ti/ \ xilinx/ diff --git a/drivers/phy/thead/Kconfig b/drivers/phy/thead/Kconfig new file mode 100644 index 00000000000000..14012db5973c4b --- /dev/null +++ b/drivers/phy/thead/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +config PHY_TH1520_USB + tristate "USB PHY driver for T-Head TH1520 SoC" + depends on ARCH_THEAD || COMPILE_TEST + depends on COMMON_CLK + depends on HAS_IOMEM + depends on OF + depends on RESET_CONTROLLER + select GENERIC_PHY + default ARCH_THEAD + help + Enable support for the USB PHY on the T-Head TH1520 SoC. diff --git a/drivers/phy/thead/Makefile b/drivers/phy/thead/Makefile new file mode 100644 index 00000000000000..5b459bc7004bda --- /dev/null +++ b/drivers/phy/thead/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_PHY_TH1520_USB) += phy-th1520-usb.o diff --git a/drivers/phy/thead/phy-th1520-usb.c b/drivers/phy/thead/phy-th1520-usb.c new file mode 100644 index 00000000000000..c87bd779bbb74b --- /dev/null +++ b/drivers/phy/thead/phy-th1520-usb.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS) + * + * Authors: + * Icenowy Zheng + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_SYSCON_OFFSET 0xf000 + +/* All the below registers are in the USB syscon region */ +#define USB_CLK_GATE_STS 0x0 +#define USB_LOGIC_ANALYZER_TRACE_STS0 0x4 +#define USB_LOGIC_ANALYZER_TRACE_STS1 0x8 +#define USB_GPIO 0xc +#define USB_DEBUG_STS0 0x10 +#define USB_DEBUG_STS1 0x14 +#define USB_DEBUG_STS2 0x18 +#define USBCTL_CLK_CTRL0 0x1c +#define USBPHY_CLK_CTRL1 0x20 +#define USBPHY_TEST_CTRL0 0x24 +#define USBPHY_TEST_CTRL1 0x28 +#define USBPHY_TEST_CTRL2 0x2c +#define USBPHY_TEST_CTRL3 0x30 +#define USB_SSP_EN 0x34 +#define USB_HADDR_SEL 0x38 +#define USB_SYS 0x3c +#define USB_HOST_STATUS 0x40 +#define USB_HOST_CTRL 0x44 +#define USBPHY_HOST_CTRL 0x48 +#define USBPHY_HOST_STATUS 0x4c +#define USB_TEST_REG0 0x50 +#define USB_TEST_REG1 0x54 +#define USB_TEST_REG2 0x58 +#define USB_TEST_REG3 0x5c + +#define USB_SYS_COMMONONN BIT(0) + +#define USB_SSP_EN_REF_SSP_EN BIT(0) + +struct th1520_usb_phy { + struct platform_device *pdev; + struct phy *phy; + struct regmap *regmap; + struct clk *ref_clk; + struct reset_control *phy_reset; +}; + +static int th1520_usb_phy_init(struct phy *phy) +{ + struct th1520_usb_phy *th1520_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(th1520_phy->ref_clk); + if (ret) + return ret; + + ret = reset_control_assert(th1520_phy->phy_reset); + if (ret) + goto err_disable_clk; + + /* + * Do some initial PHY setup: + * - Set COMMONONN to allow the PHY to automatically power down. + * - Set REF_SSP_EN to enable feeding reference clock to SuperSpeed + * PHY clock PLL. + */ + regmap_set_bits(th1520_phy->regmap, USB_SYS, USB_SYS_COMMONONN); + regmap_set_bits(th1520_phy->regmap, USB_SSP_EN, USB_SSP_EN_REF_SSP_EN); + + ret = reset_control_deassert(th1520_phy->phy_reset); + if (ret) + goto err_disable_clk; + + udelay(10); + + return 0; + +err_disable_clk: + clk_disable_unprepare(th1520_phy->ref_clk); + return ret; +} + +static int th1520_usb_phy_exit(struct phy *phy) +{ + struct th1520_usb_phy *th1520_phy = phy_get_drvdata(phy); + int ret; + + ret = reset_control_assert(th1520_phy->phy_reset); + if (ret) + return ret; + + clk_disable_unprepare(th1520_phy->ref_clk); + + return 0; +} + +static const struct phy_ops th1520_usb_phy_ops = { + .init = th1520_usb_phy_init, + .exit = th1520_usb_phy_exit, + .owner = THIS_MODULE, +}; + +static const struct regmap_config phy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = USB_TEST_REG3, +}; + +static int th1520_usb_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct th1520_usb_phy *th1520_phy; + struct reset_control *bus_reset; + void __iomem *base; + int ret; + + th1520_phy = devm_kzalloc(dev, sizeof(*th1520_phy), GFP_KERNEL); + if (!th1520_phy) + return -ENOMEM; + + th1520_phy->pdev = pdev; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + th1520_phy->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(th1520_phy->ref_clk)) + return PTR_ERR(th1520_phy->ref_clk); + + /* De-assert the bus reset and leave it that way */ + bus_reset = devm_reset_control_get_exclusive_deasserted(dev, "bus"); + if (IS_ERR(bus_reset)) + return PTR_ERR(bus_reset); + + th1520_phy->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + if (IS_ERR(th1520_phy->phy_reset)) + return PTR_ERR(th1520_phy->phy_reset); + + /* + * Schematics of several boards (Lichee Module 4A/Milk-V Meles) + * describe this power rail as always-on. + */ + ret = devm_regulator_get_enable(dev, "avdd33-usb3"); + if (ret) + return ret; + + th1520_phy->regmap = devm_regmap_init_mmio_clk(dev, "bus", + base + USB_SYSCON_OFFSET, + &phy_regmap_config); + if (IS_ERR(th1520_phy->regmap)) + return dev_err_probe(dev, PTR_ERR(th1520_phy->regmap), + "Failed to init regmap\n"); + + th1520_phy->phy = devm_phy_create(dev, dev->of_node, &th1520_usb_phy_ops); + if (IS_ERR(th1520_phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(th1520_phy->phy); + } + + phy_set_drvdata(th1520_phy->phy, th1520_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id th1520_usb_phy_of_table[] = { + { .compatible = "thead,th1520-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, th1520_usb_phy_of_table); + +static struct platform_driver th1520_usb_phy_driver = { + .driver = { + .name = "th1520-usb-phy", + .of_match_table = th1520_usb_phy_of_table, + }, + .probe = th1520_usb_phy_probe, +}; + +module_platform_driver(th1520_usb_phy_driver); + +MODULE_DESCRIPTION("T-Head TH1520 USB PHY driver"); +MODULE_LICENSE("GPL"); From eedd594fa136d159911f762dc56fc205e2af3742 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:04 +0800 Subject: [PATCH 193/521] FROMLIST: riscv: dts: thead: add device nodes for USB The TH1520 SoC contains a Synopsys DesignWare Cores SuperSpeed USB3.0 Dual Role Device controller in addition to a USB2+USB3 combo PHY based on Synopsys USB3.0 FemtoPHY. Add device tree nodes for them. The USB controller is quite generic, new and properly configured during silicon design, but the PHY is a little quirky. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-7-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- arch/riscv/boot/dts/thead/th1520.dtsi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index f543298614960d..f6049dadc3d33f 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -351,6 +351,20 @@ status = "disabled"; }; + usb: usb@ffe7040000 { + compatible = "snps,dwc3"; + reg = <0xff 0xe7040000 0x0 0x10000>; + interrupts = <68 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_misc CLK_USB>, + <&clk_misc CLK_USB_CTL_REF>, + <&clk_misc CLK_USB_SUSPEND>; + clock-names = "bus_early", "ref", "suspend"; + resets = <&rst_misc TH1520_RESET_ID_USB3_VCC>; + phys = <&usb_phy>; + phy-names = "usb3-phy"; + status = "disabled"; + }; + gmac1: ethernet@ffe7060000 { compatible = "thead,th1520-gmac", "snps,dwmac-3.70a"; reg = <0xff 0xe7060000 0x0 0x2000>, <0xff 0xec004000 0x0 0x1000>; @@ -586,6 +600,19 @@ #clock-cells = <1>; }; + usb_phy: phy@ffec030000 { + compatible = "thead,th1520-usb-phy"; + reg = <0xff 0xec030000 0x0 0x10000>; + clocks = <&clk_misc CLK_USB>, + <&clk_misc CLK_USB_PHY_REF>; + clock-names = "bus", "ref"; + resets = <&rst_misc TH1520_RESET_ID_USB3_APB>, + <&rst_misc TH1520_RESET_ID_USB3_PHY>; + reset-names = "bus", "phy"; + #phy-cells = <0>; + status = "disabled"; + }; + rst_vp: reset-controller@ffecc30000 { compatible = "thead,th1520-reset-vp"; reg = <0xff 0xecc30000 0x0 0x14>; From c1958b940511ecccdd86fecacfe345db68db7b1b Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:06 +0800 Subject: [PATCH 194/521] FROMLIST: dt-bindings: usb: vialab,vl817: allow ports property As a USB hub device, VL817 can surely be connected to external USB connectors. The binding for such connectors connection is already described in the generic usb-hub.yaml binding with ports subnode, but it's not yet allowed in the VL817 binding. Switch the reference binding from usb-device.yaml to usb-hub.yaml (which recursively references usb-device.yaml and contains definition for ports subnode) and allow ports subnode in VL817 binding. Cc: Anand Moon Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-9-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- Documentation/devicetree/bindings/usb/vialab,vl817.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/vialab,vl817.yaml b/Documentation/devicetree/bindings/usb/vialab,vl817.yaml index c815010ba9c2e8..7387f4fae54d1e 100644 --- a/Documentation/devicetree/bindings/usb/vialab,vl817.yaml +++ b/Documentation/devicetree/bindings/usb/vialab,vl817.yaml @@ -10,7 +10,7 @@ maintainers: - Anand Moon allOf: - - $ref: usb-device.yaml# + - $ref: usb-hub.yaml# properties: compatible: @@ -34,6 +34,8 @@ properties: description: phandle to the peer hub on the controller. + ports: true + required: - compatible - reg From 98ca6974522df3d4b397c052d41de655e7043d89 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:07 +0800 Subject: [PATCH 195/521] FROMLIST: riscv: dts: thead: lpi4a: sort nodes Although "D" and "H" are earlier in the alphabet than "P", the DPU and HDMI nodes were added after PADCTRL node in the Lichee Pi 4A device tree. Sort the nodes in this device tree. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-10-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- .../boot/dts/thead/th1520-lichee-pi-4a.dts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts index af6f25ebbf6071..834ee72a77eeaa 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts @@ -117,6 +117,20 @@ }; }; +&dpu { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_out_port { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &padctrl0_apsys { fan_pins: fan-0 { pwm1-pins { @@ -179,20 +193,6 @@ status = "okay"; }; -&dpu { - status = "okay"; -}; - -&hdmi { - status = "okay"; -}; - -&hdmi_out_port { - hdmi_out_con: endpoint { - remote-endpoint = <&hdmi_con_in>; - }; -}; - &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins>; From 88ec663977497ec7b30d272bb14de82e320901f7 Mon Sep 17 00:00:00 2001 From: Thomas Bonnefille Date: Thu, 7 May 2026 16:17:08 +0800 Subject: [PATCH 196/521] BACKPORT: FROMLIST: riscv: dts: thead: Add TH1520 I2C nodes Add nodes for the six I2C on the T-Head TH1520 RISCV SoC. Signed-off-by: Thomas Bonnefille Reviewed-by: Drew Fustini [Icenowy: rebase on top of v7.1-rc2] Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-11-zhengxingda@iscas.ac.cn [Han Gao: i2c1 node has been merged, so drop it from origin patch] Signed-off-by: Han Gao --- arch/riscv/boot/dts/thead/th1520.dtsi | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index f6049dadc3d33f..aa757ddd604672 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -460,6 +460,16 @@ status = "disabled"; }; + i2c0: i2c@ffe7f20000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xe7f20000 0x0 0x4000>; + interrupts = <44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + i2c1: i2c@ffe7f24000 { compatible = "thead,th1520-i2c", "snps,designware-i2c"; reg = <0xff 0xe7f24000 0x0 0x4000>; @@ -471,6 +481,16 @@ status = "disabled"; }; + i2c4: i2c@ffe7f28000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xe7f28000 0x0 0x4000>; + interrupts = <48 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C4>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + gpio@ffe7f34000 { compatible = "snps,dw-apb-gpio"; reg = <0xff 0xe7f34000 0x0 0x1000>; @@ -569,6 +589,16 @@ thead,pad-group = <3>; }; + i2c2: i2c@ffec00c000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xec00c000 0x0 0x4000>; + interrupts = <46 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + uart2: serial@ffec010000 { compatible = "snps,dw-apb-uart"; reg = <0xff 0xec010000 0x0 0x4000>; @@ -580,6 +610,16 @@ status = "disabled"; }; + i2c3: i2c@ffec014000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xec014000 0x0 0x4000>; + interrupts = <47 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C3>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + pwm: pwm@ffec01c000 { compatible = "thead,th1520-pwm"; reg = <0xff 0xec01c000 0x0 0x4000>; @@ -805,6 +845,16 @@ status = "disabled"; }; + i2c5: i2c@fff7f2c000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xf7f2c000 0x0 0x4000>; + interrupts = <49 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C5>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + timer4: timer@ffffc33000 { compatible = "snps,dw-apb-timer"; reg = <0xff 0xffc33000 0x0 0x14>; From e2599046a438132cecb8c6607fdaa7c101851de7 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 7 May 2026 16:17:09 +0800 Subject: [PATCH 197/521] BACKPORT: FROMLIST: riscv: dts: thead: Add Lichee Pi 4A IO expansions Lichee Pi 4A has 3 I2C IO expansion chips onboard, connected to the I2C0/1/3 busses. Add device tree nodes for them. Signed-off-by: Emil Renner Berthing [Icenowy: added commit description] Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-12-zhengxingda@iscas.ac.cn [Han Gao: Remove the upstream i2c1 node and set the i2c1 clock frequency to the mainline value. ] Signed-off-by: Han Gao --- .../boot/dts/thead/th1520-lichee-pi-4a.dts | 125 ++++++++++++++++-- 1 file changed, 111 insertions(+), 14 deletions(-) diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts index 834ee72a77eeaa..76c211ffdbf9c8 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts @@ -17,6 +17,9 @@ gpio3 = &gpio3; gpio4 = &gpio4; gpio5 = &aogpio; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c3 = &i2c3; serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; @@ -131,6 +134,76 @@ }; }; +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + status = "okay"; + + ioexp1: gpio@18 { + compatible = "nxp,pca9557"; + reg = <0x18>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "cam0_dvdd12", + "cam0_avdd28", + "cam0_dovdd18"; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + clock-frequency = <400000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + status = "okay"; + + ioexp2: gpio@18 { + compatible = "nxp,pca9557"; + reg = <0x18>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "", + "cam0_reset", + "cam1_reset", + "cam2_reset", + "wl_host_wake", + "bt_resetn", + "", + "bt_host_wake"; + }; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c3_pins>; + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + status = "okay"; + + ioexp3: gpio@18 { + compatible = "nxp,pca9557"; + reg = <0x18>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "tp0_rst", + "", + "", + "vcc5v_usb", + "vdd28_tp0", + "vdd33_lcd0", + "vdd18_lcd0", + "lcd0_reset"; + }; +}; + &padctrl0_apsys { fan_pins: fan-0 { pwm1-pins { @@ -144,6 +217,18 @@ }; }; + i2c3_pins: i2c3-0 { + i2c-pins { + pins = "I2C3_SCL", "I2C3_SDA"; + function = "i2c"; + bias-disable; /* external pull-up */ + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; + uart0_pins: uart0-0 { tx-pins { pins = "UART0_TXD"; @@ -167,20 +252,6 @@ }; }; -&i2c1 { - clock-frequency = <400000>; - pinctrl-names = "default"; - pinctrl-0 = <&i2c1_pins>; - status = "okay"; - - ioexp2: gpio@18 { - compatible = "nxp,pca9557"; - reg = <0x18>; - gpio-controller; - #gpio-cells = <2>; - }; -}; - &sdio1 { #address-cells = <1>; #size-cells = <0>; @@ -193,6 +264,32 @@ status = "okay"; }; +&padctrl1_apsys { + i2c0_pins: i2c0-0 { + i2c-pins { + pins = "I2C0_SCL", "I2C0_SDA"; + function = "i2c"; + bias-disable; /* external pull-up */ + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; + + i2c1_pins: i2c1-0 { + i2c-pins { + pins = "I2C1_SCL", "I2C1_SDA"; + function = "i2c"; + bias-disable; /* external pull-up */ + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins>; From b9d2a95adfc4432aae2b21296a650bd4b1a1a27d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:10 +0800 Subject: [PATCH 198/521] FROMLIST: riscv: dts: thead: enable USB3 ports on Lichee Pi 4A The Lichee Pi 4A board features an onboard VIA VL817 hub connected to the SoC's USB3 as upstream and 4 USB-3.0-capable Type-A ports as downstream. Enable SoC USB3 and the hub on Lichee Pi 4A. Signed-off-by: Icenowy Zheng Link: https://lore.kernel.org/r/20260507081710.4090814-13-zhengxingda@iscas.ac.cn Signed-off-by: Han Gao --- .../dts/thead/th1520-lichee-module-4a.dtsi | 15 ++ .../boot/dts/thead/th1520-lichee-pi-4a.dts | 230 ++++++++++++++++++ 2 files changed, 245 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi b/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi index 8e76b63e0100aa..bfda5a6b56b8f8 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi +++ b/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi @@ -20,6 +20,16 @@ device_type = "memory"; reg = <0x0 0x00000000 0x2 0x00000000>; }; + + /* TODO: Switch to AON regulator when it's available. */ + avdd33_usb3: regulator-avdd33-usb3 { + compatible = "regulator-fixed"; + regulator-name = "AVDD33_USB3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + /* Marked as always on on the schematics */ + regulator-always-on; + }; }; &osc { @@ -202,3 +212,8 @@ max-frequency = <198000000>; status = "okay"; }; + +&usb_phy { + avdd33-usb3-supply = <&avdd33_usb3>; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts index 76c211ffdbf9c8..da3425a44f63e9 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts @@ -104,6 +104,141 @@ cooling-levels = <0 66 196 255>; }; + hub_5v: regulator-hub-5v { + compatible = "regulator-fixed"; + regulator-name = "HUB_5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&ioexp3 3 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + vcc5v_usb: regulator-vcc5v-usb { + compatible = "regulator-fixed"; + regulator-name = "VCC5V_USB"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + enable-active-high; + /* + * Workaround for Linux currently being not able to power on + * Vbus for USB Type-A connectors. + */ + regulator-always-on; + }; + + connector-usb-a-1 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_1_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port1_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_1_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port1_ep>; + }; + }; + }; + }; + + connector-usb-a-2 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_2_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port2_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_2_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port2_ep>; + }; + }; + }; + }; + + connector-usb-a-3 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_3_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port3_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_3_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port3_ep>; + }; + }; + }; + }; + + connector-usb-a-4 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_4_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port4_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_4_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port4_ep>; + }; + }; + }; + }; +}; + +&aogpio { + /* Route USB2 to the onboard hub for normal operation */ + sel-usb-hub-hog { + gpio-hog; + gpios = <4 GPIO_ACTIVE_HIGH>; + output-high; + }; }; &padctrl1_apsys { @@ -295,3 +430,98 @@ pinctrl-0 = <&uart0_pins>; status = "okay"; }; + +&usb { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + hub_hs: hub@1 { + compatible = "usb2109,2817"; + reg = <1>; + peer-hub = <&hub_ss>; + vdd-supply = <&hub_5v>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + hub_hs_port1_ep: endpoint { + remote-endpoint = <&usb_a_1_hs_ep>; + }; + }; + + port@2 { + reg = <2>; + + hub_hs_port2_ep: endpoint { + remote-endpoint = <&usb_a_2_hs_ep>; + }; + }; + + port@3 { + reg = <3>; + + hub_hs_port3_ep: endpoint { + remote-endpoint = <&usb_a_3_hs_ep>; + }; + }; + + port@4 { + reg = <4>; + + hub_hs_port4_ep: endpoint { + remote-endpoint = <&usb_a_4_hs_ep>; + }; + }; + }; + }; + + hub_ss: hub@2 { + compatible = "usb2109,817"; + reg = <2>; + peer-hub = <&hub_hs>; + vdd-supply = <&hub_5v>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + hub_ss_port1_ep: endpoint { + remote-endpoint = <&usb_a_1_ss_ep>; + }; + }; + + port@2 { + reg = <2>; + + hub_ss_port2_ep: endpoint { + remote-endpoint = <&usb_a_2_ss_ep>; + }; + }; + + port@3 { + reg = <3>; + + hub_ss_port3_ep: endpoint { + remote-endpoint = <&usb_a_3_ss_ep>; + }; + }; + + port@4 { + reg = <4>; + + hub_ss_port4_ep: endpoint { + remote-endpoint = <&usb_a_4_ss_ep>; + }; + }; + }; + }; +}; From 556bc50e68dae5a5bee612276f7d4dbd31c7324a Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 8 May 2026 15:25:24 +0800 Subject: [PATCH 199/521] FROMLIST: i2c: spacemit: configure ILCR/IWCR for accurate SCL frequency The SpacemiT I2C controller's SCL (Serial Clock Line) frequency for master mode operations is determined by the ILCR (I2C Load Count Register). Previously, the driver relied on the hardware's reset default values for this register. The hardware's default ILCR values (SLV=0x156, FLV=0x5d) yield SCL frequencies lower than intended. For example, with the default 31.5 MHz input clock, these default settings result in an SCL frequency of approximately 93 kHz (standard mode) when targeting 100 kHz, and approximately 338 kHz (fast mode) when targeting 400 kHz. These frequencies are below the 100 kHz/400 kHz nominal speeds. This patch integrates the SCL frequency management into the Common Clock Framework (CCF). Specifically, the ILCR register, which acts as a frequency divider for the SCL clock, is now registered as a managed clock (scl_clk) within the CCF. The actual hardware timing formulas are: - standard mode: SCL = FCLK / (2 * SLV + 8) - fast mode: SCL = FCLK / (2 * FLV + 10) These formulas are only valid when the IWCR (Wait Count Register) is programmed to 0x142A, a value specified by the I2C IP designer. The driver now initializes IWCR to this value during controller init. Reviewed-by: Yixun Lan Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260508-k1-i2c-ilcr-v7-1-8c2dde5c3ed5@linux.spacemit.com Signed-off-by: Han Gao --- drivers/i2c/busses/Kconfig | 2 +- drivers/i2c/busses/i2c-k1.c | 174 ++++++++++++++++++++++++++++++++++-- 2 files changed, 167 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 30cb64ed431092..6a91cc4fe4db59 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -793,7 +793,7 @@ config I2C_JZ4780 config I2C_K1 tristate "SpacemiT K1 I2C adapter" depends on ARCH_SPACEMIT || COMPILE_TEST - depends on OF + depends on OF && COMMON_CLK default ARCH_SPACEMIT help This option enables support for the I2C interface on the SpacemiT K1 diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 9152cf436bea08..c6fe2052e4799f 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -4,7 +4,9 @@ */ #include +#include #include +#include #include #include #include @@ -17,6 +19,8 @@ #define SPACEMIT_ISR 0x4 /* Status register */ #define SPACEMIT_IDBR 0xc /* Data buffer register */ #define SPACEMIT_IRCR 0x18 /* Reset cycle counter */ +#define SPACEMIT_ILCR 0x10 /* Load Count Register */ +#define SPACEMIT_IWCR 0x14 /* Wait Count Register */ #define SPACEMIT_IBMR 0x1c /* Bus monitor register */ /* SPACEMIT_ICR register fields */ @@ -88,6 +92,19 @@ #define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */ #define SPACEMIT_BMR_SCL BIT(1) /* SCL line level */ +#define SPACEMIT_LCR_LV_STANDARD_MASK GENMASK(8, 0) +#define SPACEMIT_LCR_LV_FAST_MASK GENMASK(17, 9) + +/* SPACEMIT_IWCR register fields */ +#define SPACEMIT_WCR_COUNT GENMASK(4, 0) +#define SPACEMIT_WCR_HS_COUNT1 GENMASK(9, 5) +#define SPACEMIT_WCR_HS_COUNT2 GENMASK(14, 10) + +/* Required by I2C IP for correct SCL timing */ +#define SPACEMIT_IWCR_INIT_VALUE (FIELD_PREP(SPACEMIT_WCR_COUNT, 10) | \ + FIELD_PREP(SPACEMIT_WCR_HS_COUNT1, 1) | \ + FIELD_PREP(SPACEMIT_WCR_HS_COUNT2, 5)) + /* i2c bus recover timeout: us */ #define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000 @@ -109,11 +126,20 @@ enum spacemit_i2c_state { SPACEMIT_STATE_WRITE, }; +enum spacemit_i2c_mode { + SPACEMIT_MODE_STANDARD, + SPACEMIT_MODE_FAST +}; + /* i2c-spacemit driver's main struct */ struct spacemit_i2c_dev { struct device *dev; struct i2c_adapter adapt; + struct clk_hw scl_clk_hw; + struct clk *scl_clk; + enum spacemit_i2c_mode mode; + /* hardware resources */ void __iomem *base; int irq; @@ -135,6 +161,85 @@ struct spacemit_i2c_dev { u32 status; }; +static void spacemit_i2c_scl_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +/* + * Calculate the ILCR divider value (lv) from the target SCL rate. + * + * Hardware timing formulas: + * - standard mode: SCL = FCLK / (2 * SLV + 8) + * - fast mode: SCL = FCLK / (2 * FLV + 10) + */ +static u32 spacemit_i2c_calc_lv(struct spacemit_i2c_dev *i2c, + unsigned long parent_rate, + unsigned long target_rate) +{ + u32 offset, denom; + + offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10; + denom = DIV_ROUND_CLOSEST(parent_rate, target_rate); + + return (denom <= offset) ? 0 : DIV_ROUND_CLOSEST(denom - offset, 2); +} + +static int spacemit_i2c_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); + u32 lv, lcr, mask; + + lv = spacemit_i2c_calc_lv(i2c, parent_rate, rate); + + mask = (i2c->mode == SPACEMIT_MODE_STANDARD) ? + SPACEMIT_LCR_LV_STANDARD_MASK : SPACEMIT_LCR_LV_FAST_MASK; + + lcr = readl(i2c->base + SPACEMIT_ILCR); + lcr &= ~mask; + lcr |= field_prep(mask, lv); + writel(lcr, i2c->base + SPACEMIT_ILCR); + + return 0; +} + +static int spacemit_i2c_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); + u32 lv, offset; + + lv = spacemit_i2c_calc_lv(i2c, req->best_parent_rate, req->rate); + offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10; + req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, lv * 2 + offset); + + return 0; +} + +static unsigned long spacemit_i2c_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); + u32 lcr, lv = 0; + + lcr = readl(i2c->base + SPACEMIT_ILCR); + + if (i2c->mode == SPACEMIT_MODE_STANDARD) { + lv = FIELD_GET(SPACEMIT_LCR_LV_STANDARD_MASK, lcr); + return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 8); + } + + lv = FIELD_GET(SPACEMIT_LCR_LV_FAST_MASK, lcr); + return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 10); +} + +static const struct clk_ops spacemit_i2c_clk_ops = { + .set_rate = spacemit_i2c_clk_set_rate, + .determine_rate = spacemit_i2c_clk_determine_rate, + .recalc_rate = spacemit_i2c_clk_recalc_rate, +}; + static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c) { u32 val; @@ -153,6 +258,28 @@ static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c) writel(val, i2c->base + SPACEMIT_ICR); } +static int spacemit_i2c_register_scl_clk(struct spacemit_i2c_dev *i2c) +{ + struct clk_init_data init = {}; + char name[64]; + int ret; + + ret = snprintf(name, sizeof(name), "%s_scl_clk", dev_name(i2c->dev)); + if (ret >= ARRAY_SIZE(name)) + dev_warn(i2c->dev, "scl clock name truncated"); + + init.name = name; + init.ops = &spacemit_i2c_clk_ops; + init.parent_data = (struct clk_parent_data[]) { + { .fw_name = "func" }, + }; + init.num_parents = 1; + + i2c->scl_clk_hw.init = &init; + + return devm_clk_hw_register(i2c->dev, &i2c->scl_clk_hw); +} + static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c) { writel(SPACEMIT_CR_UR, i2c->base + SPACEMIT_ICR); @@ -286,7 +413,7 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) val |= SPACEMIT_CR_MSDIE; } - if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ) + if (i2c->mode == SPACEMIT_MODE_FAST) val |= SPACEMIT_CR_MODE_FAST; /* disable response to general call */ @@ -309,6 +436,14 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) writel(val, i2c->base + SPACEMIT_IRCR); spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); + + /* + * Initialize IWCR to the value specified by the I2C IP designer. + * The SCL frequency formulas (SCL = FCLK / (2*SLV+8) for standard + * mode, SCL = FCLK / (2*FLV+10) for fast mode) are only valid when + * IWCR contains this specific value. + */ + writel(SPACEMIT_IWCR_INIT_VALUE, i2c->base + SPACEMIT_IWCR); } static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c) @@ -703,14 +838,15 @@ static int spacemit_i2c_probe(struct platform_device *pdev) dev_warn(dev, "failed to read clock-frequency property: %d\n", ret); /* For now, this driver doesn't support high-speed. */ - if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) { - dev_warn(dev, "unsupported clock frequency %u; using %u\n", - i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ); + if (i2c->clock_freq > SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ && + i2c->clock_freq <= SPACEMIT_I2C_MAX_FAST_MODE_FREQ) { + i2c->mode = SPACEMIT_MODE_FAST; + } else if (i2c->clock_freq && i2c->clock_freq <= SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { + i2c->mode = SPACEMIT_MODE_STANDARD; + } else { + dev_warn(i2c->dev, "invalid clock-frequency, fallback to fast mode"); + i2c->mode = SPACEMIT_MODE_FAST; i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ; - } else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { - dev_warn(dev, "unsupported clock frequency %u; using %u\n", - i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ); - i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ; } i2c->dev = &pdev->dev; @@ -732,6 +868,15 @@ static int spacemit_i2c_probe(struct platform_device *pdev) if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock"); + ret = spacemit_i2c_register_scl_clk(i2c); + if (ret) + return dev_err_probe(dev, ret, "failed to register scl clock\n"); + + i2c->scl_clk = devm_clk_hw_get_clk(dev, &i2c->scl_clk_hw, "scl"); + if (IS_ERR(i2c->scl_clk)) + return dev_err_probe(dev, PTR_ERR(i2c->scl_clk), + "failed to get scl clock\n"); + clk = devm_clk_get_enabled(dev, "bus"); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock"); @@ -741,6 +886,19 @@ static int spacemit_i2c_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(rst), "failed to acquire deasserted reset\n"); + ret = clk_set_rate(i2c->scl_clk, i2c->clock_freq); + if (ret) + return dev_err_probe(dev, ret, "failed to set rate for SCL clock"); + + ret = clk_prepare_enable(i2c->scl_clk); + if (ret) + return dev_err_probe(dev, ret, "failed to prepare and enable clock"); + + ret = devm_add_action_or_reset(dev, spacemit_i2c_scl_clk_disable_unprepare, + i2c->scl_clk); + if (ret) + return ret; + spacemit_i2c_reset(i2c); i2c_set_adapdata(&i2c->adapt, i2c); From 477e5402f48dbd53093ed809dc6e3f9eda8dedd9 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 8 May 2026 15:25:25 +0800 Subject: [PATCH 200/521] FROMLIST: i2c: spacemit: drop warning when clock-frequency property is absent The clock-frequency property is optional according to the DT binding. Do not emit a warning when the property is missing and fall back to the default frequency instead. Reviewed-by: Alex Elder Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260508-k1-i2c-ilcr-v7-2-8c2dde5c3ed5@linux.spacemit.com Signed-off-by: Han Gao --- drivers/i2c/busses/i2c-k1.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index c6fe2052e4799f..7cf5c05a20d26b 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -833,9 +833,7 @@ static int spacemit_i2c_probe(struct platform_device *pdev) if (!i2c) return -ENOMEM; - ret = of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq); - if (ret && ret != -EINVAL) - dev_warn(dev, "failed to read clock-frequency property: %d\n", ret); + of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq); /* For now, this driver doesn't support high-speed. */ if (i2c->clock_freq > SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ && @@ -844,7 +842,7 @@ static int spacemit_i2c_probe(struct platform_device *pdev) } else if (i2c->clock_freq && i2c->clock_freq <= SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { i2c->mode = SPACEMIT_MODE_STANDARD; } else { - dev_warn(i2c->dev, "invalid clock-frequency, fallback to fast mode"); + dev_info(dev, "clock-frequency not set or out of range, using fast mode\n"); i2c->mode = SPACEMIT_MODE_FAST; i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ; } From b54056b036142392c053b1299aad0d44ffd0ec3b Mon Sep 17 00:00:00 2001 From: Fangyu Yu Date: Tue, 12 May 2026 15:41:42 +0800 Subject: [PATCH 201/521] FROMLIST: iommupt: Encode IOMMU_MMIO/IOMMU_CACHE via RISC-V Svpbmt bits When the RISC-V IOMMU page table format support Svpbmt, PBMT provides a way to tag mappings with page-based memory types. Encode memory type via PBMT in RISC-V IOMMU PTEs: - IOMMU_MMIO -> PBMT=IO - !IOMMU_MMIO && !IOMMU_CACHE -> PBMT=NC - otherwise -> PBMT=Normal (PBMT=0) Only touch PBMT when PT_FEAT_RISCV_SVPBMT is advertised. Reviewed-by: Jason Gunthorpe Reviewed-by: Anup Patel Reviewed-by: Guo Ren Reviewed-by: Nutty Liu Reviewed-by: Kevin Tian Signed-off-by: Fangyu Yu Link: https://lore.kernel.org/r/20260512074142.16356-3-fangyu.yu@linux.alibaba.com Signed-off-by: Han Gao --- drivers/iommu/generic_pt/fmt/riscv.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/generic_pt/fmt/riscv.h b/drivers/iommu/generic_pt/fmt/riscv.h index a7fef6266a36a6..ae9a76514416c2 100644 --- a/drivers/iommu/generic_pt/fmt/riscv.h +++ b/drivers/iommu/generic_pt/fmt/riscv.h @@ -64,6 +64,8 @@ enum { RISCVPT_PPN64 = GENMASK_ULL(53, 10), RISCVPT_PPN64_64K = GENMASK_ULL(53, 14), RISCVPT_PBMT = GENMASK_ULL(62, 61), + RISCVPT_NC = BIT_ULL(61), + RISCVPT_IO = BIT_ULL(62), RISCVPT_N = BIT_ULL(63), /* Svnapot encodings for ppn[0] */ @@ -201,7 +203,8 @@ static inline void riscvpt_attr_from_entry(const struct pt_state *pts, { attrs->descriptor_bits = pts->entry & (RISCVPT_R | RISCVPT_W | RISCVPT_X | RISCVPT_U | - RISCVPT_G | RISCVPT_A | RISCVPT_D); + RISCVPT_G | RISCVPT_A | RISCVPT_D | RISCVPT_NC | + RISCVPT_IO); } #define pt_attr_from_entry riscvpt_attr_from_entry @@ -237,6 +240,12 @@ static inline int riscvpt_iommu_set_prot(struct pt_common *common, pte |= RISCVPT_R; if (!(iommu_prot & IOMMU_NOEXEC)) pte |= RISCVPT_X; + if (common->features & BIT(PT_FEAT_RISCV_SVPBMT)) { + if (iommu_prot & IOMMU_MMIO) + pte |= RISCVPT_IO; + else if (!(iommu_prot & IOMMU_CACHE)) + pte |= RISCVPT_NC; + } /* Caller must specify a supported combination of flags */ if (unlikely((pte & (RISCVPT_X | RISCVPT_W | RISCVPT_R)) == 0)) From 06442f0338438225da0331eeb039e129c0726b9f Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:00:21 +0800 Subject: [PATCH 202/521] FROMLIST: PCI: spacemit-k1: Add device data support To reuse the K1 PCIe driver logic for K3 PCIe controller, add device data to handle the K1 specific logic and make room for the incoming logic for K3. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260709040027.958400-2-inochiama@gmail.com Signed-off-by: Han Gao --- drivers/pci/controller/dwc/pcie-spacemit-k1.c | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index be20a520255b68..f6ae8ff3589a9d 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -49,8 +49,17 @@ #define PCIE_CONTROL_LOGIC 0x0004 #define PCIE_SOFT_RESET BIT(0) +struct k1_pcie; + +struct k1_pcie_device_data { + const struct dw_pcie_host_ops *host_ops; + const struct dw_pcie_ops *ops; + int (*parse_port)(struct k1_pcie *k1); +}; + struct k1_pcie { struct dw_pcie pci; + const struct k1_pcie_device_data *data; struct phy *phy; void __iomem *link; struct regmap *pmu; /* Errors ignored; MMIO-backed regmap */ @@ -278,14 +287,21 @@ static int k1_pcie_parse_port(struct k1_pcie *k1) static int k1_pcie_probe(struct platform_device *pdev) { + const struct k1_pcie_device_data *data; struct device *dev = &pdev->dev; struct k1_pcie *k1; int ret; + data = device_get_match_data(dev); + if (!data) + return -ENODEV; + k1 = devm_kzalloc(dev, sizeof(*k1), GFP_KERNEL); if (!k1) return -ENOMEM; + k1->data = data; + k1->pmu = syscon_regmap_lookup_by_phandle_args(dev_of_node(dev), SYSCON_APMU, 1, &k1->pmu_off); @@ -299,11 +315,11 @@ static int k1_pcie_probe(struct platform_device *pdev) "failed to map \"link\" registers\n"); k1->pci.dev = dev; - k1->pci.ops = &k1_pcie_ops; + k1->pci.ops = data->ops; k1->pci.pp.num_vectors = MAX_MSI_IRQS; dw_pcie_cap_set(&k1->pci, REQ_RES); - k1->pci.pp.ops = &k1_pcie_host_ops; + k1->pci.pp.ops = data->host_ops; /* Hold the PHY in reset until we start the link */ regmap_set_bits(k1->pmu, k1->pmu_off + PCIE_CLK_RESET_CONTROL, @@ -320,7 +336,7 @@ static int k1_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, k1); - ret = k1_pcie_parse_port(k1); + ret = data->parse_port(k1); if (ret) return dev_err_probe(dev, ret, "failed to parse root port\n"); @@ -338,8 +354,14 @@ static void k1_pcie_remove(struct platform_device *pdev) dw_pcie_host_deinit(&k1->pci.pp); } +static const struct k1_pcie_device_data k1_pcie_device_data = { + .host_ops = &k1_pcie_host_ops, + .ops = &k1_pcie_ops, + .parse_port = k1_pcie_parse_port, +}; + static const struct of_device_id k1_pcie_of_match_table[] = { - { .compatible = "spacemit,k1-pcie", }, + { .compatible = "spacemit,k1-pcie", .data = &k1_pcie_device_data}, { } }; From b3d2bf56810420ad65747691c54fb2975528c842 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:00:22 +0800 Subject: [PATCH 203/521] FROMLIST: PCI: spacemit-k1: Add multiple PHY handles support The PCIe controller on Spacemit K3 may use multiple PHYs at the same time. The feature is not support by the current driver. So extend the PHY definition to support multiple PHY handles. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260709040027.958400-3-inochiama@gmail.com Signed-off-by: Han Gao --- drivers/pci/controller/dwc/pcie-spacemit-k1.c | 70 ++++++++++++++++--- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index f6ae8ff3589a9d..e22ecbd095798f 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -55,12 +55,14 @@ struct k1_pcie_device_data { const struct dw_pcie_host_ops *host_ops; const struct dw_pcie_ops *ops; int (*parse_port)(struct k1_pcie *k1); + unsigned int max_phy_count; }; struct k1_pcie { struct dw_pcie pci; const struct k1_pcie_device_data *data; - struct phy *phy; + struct phy **phy; + unsigned int phy_count; void __iomem *link; struct regmap *pmu; /* Errors ignored; MMIO-backed regmap */ u32 pmu_off; @@ -119,6 +121,54 @@ static void k1_pcie_disable_resources(struct k1_pcie *k1) clk_bulk_disable_unprepare(ARRAY_SIZE(pci->app_clks), pci->app_clks); } +static int k1_pcie_get_phy_handle(struct k1_pcie *k1, struct device_node *node) +{ + const struct k1_pcie_device_data *data = k1->data; + struct device *dev = k1->pci.dev; + unsigned int i; + + k1->phy = devm_kmalloc_array(dev, data->max_phy_count, + sizeof(*k1->phy), GFP_KERNEL); + if (!k1->phy) + return -ENOMEM; + + for (i = 0; i < data->max_phy_count; i++) { + k1->phy[i] = devm_of_phy_get_by_index(dev, node, i); + if (IS_ERR(k1->phy[i])) { + if (PTR_ERR(k1->phy[i]) == -ENODEV) + break; + + return PTR_ERR(k1->phy[i]); + } + } + + k1->phy_count = i; + if (k1->phy_count == 0) + return -EINVAL; + + return 0; +} + +static int k1_pcie_enable_phy(struct k1_pcie *k1) +{ + unsigned int i; + int ret; + + for (i = 0; i < k1->phy_count; i++) { + ret = phy_init(k1->phy[i]); + if (ret) + goto err_phy; + } + + return 0; + +err_phy: + while (i--) + phy_exit(k1->phy[i]); + + return ret; +} + /* FIXME: Disable ASPM L1 to avoid errors reported on some NVMe drives */ static void k1_pcie_disable_aspm_l1(struct k1_pcie *k1) { @@ -174,7 +224,7 @@ static int k1_pcie_init(struct dw_pcie_rp *pp) */ regmap_set_bits(k1->pmu, reset_ctrl, DEVICE_TYPE_RC | PCIE_AUX_PWR_DET); - ret = phy_init(k1->phy); + ret = k1_pcie_enable_phy(k1); if (ret) { k1_pcie_disable_resources(k1); @@ -194,12 +244,14 @@ static void k1_pcie_deinit(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct k1_pcie *k1 = to_k1_pcie(pci); + int i; /* Assert fundamental reset (drive PERST# low) */ regmap_set_bits(k1->pmu, k1->pmu_off + PCIE_CLK_RESET_CONTROL, PCIE_RC_PERST); - phy_exit(k1->phy); + for (i = 0; i < k1->phy_count; i++) + phy_exit(k1->phy[i]); k1_pcie_disable_resources(k1); } @@ -266,23 +318,18 @@ static int k1_pcie_parse_port(struct k1_pcie *k1) { struct device *dev = k1->pci.dev; struct device_node *root_port; - struct phy *phy; + int ret; /* We assume only one root port */ root_port = of_get_next_available_child(dev_of_node(dev), NULL); if (!root_port) return -EINVAL; - phy = devm_of_phy_get(dev, root_port, NULL); + ret = k1_pcie_get_phy_handle(k1, root_port); of_node_put(root_port); - if (IS_ERR(phy)) - return PTR_ERR(phy); - - k1->phy = phy; - - return 0; + return ret; } static int k1_pcie_probe(struct platform_device *pdev) @@ -358,6 +405,7 @@ static const struct k1_pcie_device_data k1_pcie_device_data = { .host_ops = &k1_pcie_host_ops, .ops = &k1_pcie_ops, .parse_port = k1_pcie_parse_port, + .max_phy_count = 1, }; static const struct of_device_id k1_pcie_of_match_table[] = { From 7b1e407da84dd2881c3d3bfe6a7a90d3f9c66262 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:00:23 +0800 Subject: [PATCH 204/521] FROMLIST: PCI: spacemit-k1: Add device id update helper Both K1 and K3 needs to set vendor id and device id, add a helper function to simplify this. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260709040027.958400-4-inochiama@gmail.com Signed-off-by: Han Gao --- drivers/pci/controller/dwc/pcie-spacemit-k1.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index e22ecbd095798f..31aac056b68edc 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -56,6 +56,7 @@ struct k1_pcie_device_data { const struct dw_pcie_ops *ops; int (*parse_port)(struct k1_pcie *k1); unsigned int max_phy_count; + unsigned int device_id; }; struct k1_pcie { @@ -186,6 +187,16 @@ static void k1_pcie_disable_aspm_l1(struct k1_pcie *k1) dw_pcie_dbi_ro_wr_dis(pci); } +static void k1_pcie_set_device_id(struct k1_pcie *k1) +{ + struct dw_pcie *pci = &k1->pci; + + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, PCI_VENDOR_ID_SPACEMIT); + dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, k1->data->device_id); + dw_pcie_dbi_ro_wr_dis(pci); +} + static int k1_pcie_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -201,10 +212,7 @@ static int k1_pcie_init(struct dw_pcie_rp *pp) return ret; /* Set the PCI vendor and device ID */ - dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, PCI_VENDOR_ID_SPACEMIT); - dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, PCI_DEVICE_ID_SPACEMIT_K1); - dw_pcie_dbi_ro_wr_dis(pci); + k1_pcie_set_device_id(k1); /* * Start by asserting fundamental reset (drive PERST# low). The @@ -406,6 +414,7 @@ static const struct k1_pcie_device_data k1_pcie_device_data = { .ops = &k1_pcie_ops, .parse_port = k1_pcie_parse_port, .max_phy_count = 1, + .device_id = PCI_DEVICE_ID_SPACEMIT_K1, }; static const struct of_device_id k1_pcie_of_match_table[] = { From 968e1a52b94c358239643f43b853535ab75e26d4 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:00:24 +0800 Subject: [PATCH 205/521] FROMLIST: dt-bindings: PCI: snps,dw-pcie: Add msi-parent for MSI handle check The IMSIC device on RISC-V based system does not require ID remapping for MSI. So this device only needs "msi-parent" property for IMSIC-based SoC, and the "msi-map" is not a necessary property. Add new condition for MSI handling on IMSIC based SoC. Signed-off-by: Inochi Amaoto Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260709040027.958400-5-inochiama@gmail.com Signed-off-by: Han Gao --- Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml index b3216141881c9c..91bbbc8924f608 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml @@ -27,8 +27,11 @@ allOf: - $ref: /schemas/pci/snps,dw-pcie-common.yaml# - if: not: - required: - - msi-map + anyOf: + - required: + - msi-map + - required: + - msi-parent then: properties: interrupt-names: From 28d28c69937dc5dca779b72910729162b82eaece Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:00:25 +0800 Subject: [PATCH 206/521] FROMLIST: dt-bindings: PCI: spacemit: Introduce Spacemit K3 PCIe host controller Add binding support for the PCIe controller on the SpacemiT K3 SoC. This controller is almost a standard Synopsys DesignWare PCIe IP, with some extra link and reset state control. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260709040027.958400-6-inochiama@gmail.com Signed-off-by: Han Gao --- .../bindings/pci/spacemit,k1-pcie-host.yaml | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml b/Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml index c4c00b5fcdc0c4..54817d6fd9afe0 100644 --- a/Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml @@ -14,26 +14,29 @@ description: > PCIe IP. The controller uses the DesignWare built-in MSI interrupt controller, and supports 256 MSIs. -allOf: - - $ref: /schemas/pci/snps,dw-pcie.yaml# - properties: compatible: - const: spacemit,k1-pcie + enum: + - spacemit,k1-pcie + - spacemit,k3-pcie reg: + minItems: 4 items: - description: DesignWare PCIe registers - description: ATU address space - description: PCIe configuration space - description: Link control registers + - description: Data Bus Interface (DBI) shadow registers. reg-names: + minItems: 4 items: - const: dbi - const: atu - const: config - const: link + - const: dbi2 clocks: items: @@ -66,6 +69,8 @@ properties: interrupt-names: const: msi + msi-parent: true + spacemit,apmu: $ref: /schemas/types.yaml#/definitions/phandle-array description: @@ -84,7 +89,8 @@ patternProperties: properties: phys: - maxItems: 1 + minItems: 1 + maxItems: 6 vpcie3v3-supply: description: @@ -96,13 +102,43 @@ patternProperties: unevaluatedProperties: false +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + - if: + properties: + compatible: + contains: + const: spacemit,k1-pcie + then: + properties: + reg: + maxItems: 4 + + reg-names: + maxItems: 4 + + patternProperties: + '^pcie@': + properties: + phys: + maxItems: 1 + + required: + - interrupts + - interrupt-names + else: + properties: + reg: + minItems: 5 + + reg-names: + minItems: 5 + required: - clocks - clock-names - resets - reset-names - - interrupts - - interrupt-names - spacemit,apmu unevaluatedProperties: false From f044c1fd8227e0c2a51bd2a8c80ece6fbaf5b2c8 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:00:26 +0800 Subject: [PATCH 207/521] FROMLIST: PCI: spacemit-k1: Add Spacemit K3 PCIe host controller support The PCIe controller on Spacemit K3 is almost a standard Synopsys DesignWare PCIe IP with extra link and reset control. Unlike the PCIe controller on K1, this controller supports external MSI interrupt controller and can use multiple PHYs at the same time. Add driver to support PCIe controller on Spacemit K3 PCIe. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260709040027.958400-7-inochiama@gmail.com Signed-off-by: Han Gao --- drivers/pci/controller/dwc/Kconfig | 4 +- drivers/pci/controller/dwc/pcie-spacemit-k1.c | 126 ++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index f2fde13107f2e4..53b5004a8af86f 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -439,7 +439,7 @@ config PCIE_SOPHGO_DW Sophgo SoCs. config PCIE_SPACEMIT_K1 - tristate "SpacemiT K1 PCIe controller (host mode)" + tristate "SpacemiT PCIe controller (host mode)" depends on ARCH_SPACEMIT || COMPILE_TEST depends on HAS_IOMEM select PCIE_DW_HOST @@ -447,7 +447,7 @@ config PCIE_SPACEMIT_K1 default ARCH_SPACEMIT help Enables support for the DesignWare based PCIe controller in - the SpacemiT K1 SoC operating in host mode. Three controllers + the SpacemiT SoC operating in host mode. Three controllers are available on the K1 SoC; the first of these shares a PHY with a USB 3.0 host controller (one or the other can be used). diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index 31aac056b68edc..680acc93f53957 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -23,6 +23,7 @@ #define PCI_VENDOR_ID_SPACEMIT 0x201f #define PCI_DEVICE_ID_SPACEMIT_K1 0x0001 +#define PCI_DEVICE_ID_SPACEMIT_K3 0x0002 /* Offsets and field definitions for link management registers */ #define K1_PHY_AHB_IRQ_EN 0x0000 @@ -32,8 +33,18 @@ #define SMLH_LINK_UP BIT(1) #define RDLH_LINK_UP BIT(12) +#define INTR_STATUS 0x0010 + #define INTR_ENABLE 0x0014 #define MSI_CTRL_INT BIT(11) +#define RDLH_LINK_UP_INT BIT(20) + +#define K3_PHY_AHB_IRQSTATUS_INTX 0x0008 + +#define K3_ADDR_INTR_STATUS1 0x0018 + +#define K3_CACHE_MSTR_AWCACHE_MODE GENMASK(14, 11) +#define K3_CACHE_MSTR_AWCACHE_BEHAVIOR 0xf /* Some controls require APMU regmap access */ #define SYSCON_APMU "spacemit,apmu" @@ -48,6 +59,9 @@ #define PCIE_CONTROL_LOGIC 0x0004 #define PCIE_SOFT_RESET BIT(0) +#define PCIE_PERSTN_OE BIT(24) +#define PCIE_PERSTN_OUT BIT(25) +#define PCIE_IGNORE_PERSTN BIT(31) struct k1_pcie; @@ -340,6 +354,109 @@ static int k1_pcie_parse_port(struct k1_pcie *k1) return ret; } +static int k3_pcie_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct k1_pcie *k1 = to_k1_pcie(pci); + u32 reset_ctrl = k1->pmu_off + PCIE_CLK_RESET_CONTROL; + u32 val; + int ret; + + regmap_clear_bits(k1->pmu, reset_ctrl, LTSSM_EN); + + k1_pcie_toggle_soft_reset(k1); + + ret = k1_pcie_enable_resources(k1); + if (ret) + return ret; + + regmap_set_bits(k1->pmu, reset_ctrl, PCIE_AUX_PWR_DET); + regmap_clear_bits(k1->pmu, reset_ctrl, APP_HOLD_PHY_RST); + + ret = k1_pcie_enable_phy(k1); + if (ret) { + k1_pcie_disable_resources(k1); + return ret; + } + + /* K3: Set IGNORE_PERSTN and drive PERSTN_OE high (assert reset) */ + regmap_set_bits(k1->pmu, k1->pmu_off + PCIE_CONTROL_LOGIC, + PCIE_IGNORE_PERSTN | PCIE_PERSTN_OE | PCIE_PERSTN_OUT); + usleep_range(1000, 2000); + regmap_clear_bits(k1->pmu, k1->pmu_off + PCIE_CONTROL_LOGIC, PCIE_PERSTN_OUT); + + msleep(PCIE_T_PVPERL_MS); + + /* + * Put the controller in root complex mode, and indicate that + * Vaux (3.3v) is present. + */ + regmap_set_bits(k1->pmu, k1->pmu_off + PCIE_CONTROL_LOGIC, + PCIE_PERSTN_OUT | PCIE_PERSTN_OE); + + val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); + val = u32_replace_bits(val, BIT(7), + GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC); + dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val); + + k1_pcie_set_device_id(k1); + + /* Finally, as a workaround, disable ASPM L1 */ + k1_pcie_disable_aspm_l1(k1); + + return 0; +} + +static int k3_pcie_msi_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u32 val; + + dw_pcie_dbi_ro_wr_en(pci); + + val = dw_pcie_readl_dbi(pci, COHERENCY_CONTROL_3_OFF); + val = u32_replace_bits(val, K3_CACHE_MSTR_AWCACHE_BEHAVIOR, + K3_CACHE_MSTR_AWCACHE_MODE); + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, val); + + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + +static const struct dw_pcie_host_ops k3_pcie_host_ops = { + .init = k3_pcie_init, + .deinit = k1_pcie_deinit, + .msi_init = k3_pcie_msi_host_init, +}; + +static const struct dw_pcie_ops k3_pcie_ops = { + .link_up = k1_pcie_link_up, + .start_link = k1_pcie_start_link, + .stop_link = k1_pcie_stop_link, +}; + +static void k3_pcie_clear_irq_status(struct k1_pcie *k1, + u32 *status0, u32 *status1, u32 *status2) +{ + *status0 = readl_relaxed(k1->link + K3_PHY_AHB_IRQSTATUS_INTX); + *status1 = readl_relaxed(k1->link + INTR_STATUS); + *status2 = readl_relaxed(k1->link + K3_ADDR_INTR_STATUS1); + + writel_relaxed(*status0, k1->link + K3_PHY_AHB_IRQSTATUS_INTX); + writel_relaxed(*status1, k1->link + INTR_STATUS); + writel_relaxed(*status2, k1->link + K3_ADDR_INTR_STATUS1); +} + +static int k3_pcie_parse_port(struct k1_pcie *k1) +{ + u32 status0, status1, status2; + + k3_pcie_clear_irq_status(k1, &status0, &status1, &status2); + + return k1_pcie_parse_port(k1); +} + static int k1_pcie_probe(struct platform_device *pdev) { const struct k1_pcie_device_data *data; @@ -417,8 +534,17 @@ static const struct k1_pcie_device_data k1_pcie_device_data = { .device_id = PCI_DEVICE_ID_SPACEMIT_K1, }; +static const struct k1_pcie_device_data k3_pcie_device_data = { + .host_ops = &k3_pcie_host_ops, + .ops = &k3_pcie_ops, + .parse_port = k3_pcie_parse_port, + .max_phy_count = 6, + .device_id = PCI_DEVICE_ID_SPACEMIT_K3, +}; + static const struct of_device_id k1_pcie_of_match_table[] = { { .compatible = "spacemit,k1-pcie", .data = &k1_pcie_device_data}, + { .compatible = "spacemit,k3-pcie", .data = &k3_pcie_device_data}, { } }; From 1110ed569ebde26352809dfdedd8108839368a11 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 02:58:36 +0000 Subject: [PATCH 208/521] FROMLIST: clk: spacemit: k3: fix USB2 bus clock According to SpacemiT K3's updated docs, the USB2 ahb reset and USB2 bus clock enable bit was wrongly swapped, the correct one should be: Register : APMU_USB_CLK_RES_CTRL bit[1] : usb2_port_bus_clk_en bit[0] : usb2_port_ahb_rstn Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Reported-by: Junzhong Pan Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260518-06-clk-reset-usb-fix-v1-1-14fc235e692b@kernel.org Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index cb0c4277f72a8e..03de0414496377 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -777,7 +777,7 @@ static const struct clk_parent_data sdh2_parents[] = { CCU_MUX_DIV_GATE_FC_DEFINE(sdh2_clk, sdh2_parents, APMU_SDH2_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, BIT(4), 0); -CCU_GATE_DEFINE(usb2_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(0), 0); +CCU_GATE_DEFINE(usb2_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(1), 0); CCU_GATE_DEFINE(usb3_porta_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(4), 0); CCU_GATE_DEFINE(usb3_portb_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(8), 0); CCU_GATE_DEFINE(usb3_portc_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(12), 0); From 7c93cbedcbd767aded22a9f554805edc3f9d4377 Mon Sep 17 00:00:00 2001 From: Zhengyu He Date: Sat, 11 Jul 2026 09:38:52 +0800 Subject: [PATCH 209/521] FROMLIST: riscv: dts: spacemit: k3: Add QSPI support for Pico-ITX board Enable QSPI with proper pinmux on the Pico-ITX board, and describe the NOR flash wired to it. Tested-by: Aurelien Jarno Reviewed-by: Aurelien Jarno Signed-off-by: Cody Kang Signed-off-by: Zhengyu He Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260711-k3-pico-itx-qspi-v3-v3-1-d6b37fc86c39@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 56 ++++++++++++++++++++ arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 21 ++++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 17 ++++++ 3 files changed, 94 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index b89c1521e6649b..fd2b154f275a35 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -200,6 +200,62 @@ }; }; +&qspi_cfg { + qspi-pins { + power-source = <1800>; + }; + + qspi-cs0-pins { + power-source = <1800>; + }; +}; + +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + vcc-supply = <&aldo2>; /* PMIC_VCC1V8_QSPI */ + m25p,fast-read; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootinfo@0 { + reg = <0x0 0x20000>; + }; + + fsbl@20000 { + reg = <0x20000 0x80000>; + }; + + env@a0000 { + reg = <0xa0000 0x10000>; + }; + + esos@b0000 { + reg = <0xb0000 0x100000>; + }; + + opensbi@1b0000 { + reg = <0x1b0000 0x60000>; + }; + + uboot@210000 { + reg = <0x210000 0x5f0000>; + }; + }; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_0_cfg>; diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi index 3ee1471f37985f..846d5e8cc78377 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -679,6 +679,27 @@ }; }; + /omit-if-no-ref/ + qspi_cfg: qspi-cfg { + qspi-pins { + pinmux = , /* qspi dat0 */ + , /* qspi dat1 */ + , /* qspi dat2 */ + , /* qspi dat3 */ + ; /* qspi clk */ + + bias-disable; + drive-strength = <25>; + }; + + qspi-cs0-pins { + pinmux = ; /* qspi cs0 */ + + bias-disable; + drive-strength = <25>; + }; + }; + /omit-if-no-ref/ uart0_0_cfg: uart0-0-cfg { uart0-0-pins { diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 19fc9b49668edb..9a49dd8032bf01 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -1099,6 +1099,23 @@ #clock-cells = <1>; }; + qspi: spi@d420c000 { + compatible = "spacemit,k3-qspi", + "spacemit,k1-qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0xd420c000 0x0 0x1000>, + <0x0 0xb8000000 0x0 0xc00000>; + reg-names = "QuadSPI", "QuadSPI-memory"; + clocks = <&syscon_apmu CLK_APMU_QSPI_BUS>, + <&syscon_apmu CLK_APMU_QSPI>; + clock-names = "qspi_en", "qspi"; + resets = <&syscon_apmu RESET_APMU_QSPI>, + <&syscon_apmu RESET_APMU_QSPI_BUS>; + interrupts = <117 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + syscon_apmu: system-controller@d4282800 { compatible = "spacemit,k3-syscon-apmu"; reg = <0x0 0xd4282800 0x0 0x400>; From 13de012badaf916bf07a053d38f6b1ffff33df58 Mon Sep 17 00:00:00 2001 From: Jennifer Berringer Date: Wed, 20 May 2026 07:11:50 -0400 Subject: [PATCH 210/521] FROMLIST: riscv: dts: spacemit: set console baud rate on OrangePi RV2 Set the baud rate to 115200, matching what is used by U-Boot on this platform so that the console is usable even when console options are not specified in cmdline. Fixes: bab8dea259100 ("riscv: dts: spacemit: Add OrangePi RV2 board device tree") Signed-off-by: Jennifer Berringer Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260520111150.3300707-1-jberring@redhat.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index 7c49bce427f307..07dbd292eee33e 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -22,7 +22,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; pcie_vcc3v3: regulator-pcie-vcc3v3 { From 871c7a404526a4df32a44ea9bc4d7ab5fa536772 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Tue, 2 Jun 2026 18:00:00 +0800 Subject: [PATCH 211/521] FROMLIST: riscv: dts: spacemit: enable PCIe on OrangePi R2S Enable the two RTL8125 network controllers and corresponding PHYs connected via the PCIe controllers on the OrangePi R2S. Signed-off-by: Chukun Pan Link: https://lore.kernel.org/r/20260602100000.2402784-1-amadeus@jmu.edu.cn Signed-off-by: Han Gao --- .../boot/dts/spacemit/k1-orangepi-r2s.dts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts index b13a8d6a2670e3..919e5b45110961 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts @@ -23,6 +23,14 @@ stdout-path = "serial0"; }; + pcie_vcc3v3: regulator-pcie-vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "pcie_vcc3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vcc4v0: regulator-vcc4v0 { compatible = "regulator-fixed"; regulator-name = "vcc4v0"; @@ -228,6 +236,36 @@ }; }; +&pcie1_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_3_cfg>; + status = "okay"; +}; + +&pcie1_port { + phys = <&pcie1_phy>; + vpcie3v3-supply = <&pcie_vcc3v3>; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_4_cfg>; + status = "okay"; +}; + +&pcie2_port { + phys = <&pcie2_phy>; + vpcie3v3-supply = <&pcie_vcc3v3>; +}; + +&pcie2 { + status = "okay"; +}; + &pdma { status = "okay"; }; From c1b715f391bc2fda817b9a16d42749e1e53f80d0 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Sun, 14 Jun 2026 14:28:11 +0200 Subject: [PATCH 212/521] FROMLIST: riscv: dts: spacemit: k1-musepi-pro: add cpu scaling Enable CPU DVFS by including the OPP table and wiring the CPU nodes to the CPU regulator supply. Signed-off-by: Andre Heider Link: https://lore.kernel.org/r/20260614122812.2287506-1-a.heider@gmail.com Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k1-musepi-pro.dts | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index 0d809e4ad3b1f7..1b425011f1231d 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -9,6 +9,7 @@ #include "k1.dtsi" #include "k1-pinctrl.dtsi" +#include "k1-opp.dtsi" / { model = "SpacemiT MusePi Pro"; @@ -99,6 +100,38 @@ status = "okay"; }; +&cpu_0 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_1 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_2 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_3 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_4 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_5 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_6 { + cpu-supply = <&buck1_0v9>; +}; + +&cpu_7 { + cpu-supply = <&buck1_0v9>; +}; + ð0 { phy-handle = <&rgmii0>; phy-mode = "rgmii-id"; @@ -178,7 +211,7 @@ dldoin2-supply = <&buck5>; regulators { - buck1 { + buck1_0v9: buck1 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3450000>; regulator-ramp-delay = <5000>; From fbfb32af1f12e988afac9a230be6b492410a4b63 Mon Sep 17 00:00:00 2001 From: Shung-Hsi Yu Date: Tue, 18 Feb 2025 12:48:01 +0800 Subject: [PATCH 213/521] FROMLIST: powerpc: use __clang__ instead of CONFIG_CC_IS_CLANG Due to include chain (below), powerpc's asm-compat.h is part of UAPI, thus it should use the __clang__ macro to directly detect whether Clang is used rather then relying on the kernel config setting. The later is unreliable because the userspace tools that uses UAPI may be compile with a different compiler than the one used for the kernel, leading to incorrect constrain selection (see link for an example of such). include/uapi/linux/ptrace.h arch/powerpc/include/asm/ptrace.h arch/powerpc/include/asm/paca.h arch/powerpc/include/asm/atomic.h arch/powerpc/include/asm/asm-compat.h Link: https://github.com/iovisor/bcc/issues/5172 Signed-off-by: Shung-Hsi Yu Link: https://lore.kernel.org/all/20250218044802.17302-1-shung-hsi.yu@suse.com/ Signed-off-by: Mingcong Bai --- arch/powerpc/include/asm/asm-compat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/asm-compat.h b/arch/powerpc/include/asm/asm-compat.h index f48e644900a26e..34f8740909a945 100644 --- a/arch/powerpc/include/asm/asm-compat.h +++ b/arch/powerpc/include/asm/asm-compat.h @@ -37,7 +37,7 @@ #define STDX_BE stringify_in_c(stdbrx) #endif -#ifdef CONFIG_CC_IS_CLANG +#ifdef __clang__ #define DS_FORM_CONSTRAINT "Z<>" #else #define DS_FORM_CONSTRAINT "YZ<>" From 3f6073b1ca776a675ac9dc35575b964450803726 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 2 Jun 2026 16:14:51 +0800 Subject: [PATCH 214/521] FROMLIST: arm64: dts: qcom: sc8280xp: set GPI DMA channels according to DSDT Some bugs of the GPI driver exhibits a fact that some GPI interfaces aren't available to HLOS, and accessing them leads to system stucks / resets [1] [2]. This patchset sets the DMA channel mask of sc8280xp device trees to the values indicated by the DSDTs of the corresponding devices. As different devices seem to have different allowed DMA channels, the value in the SoC DTSI file is removed, to prevent new DTS's from directly using these broken values. [1] https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2142403 [2] https://discussion.fedoraproject.org/t/fedora-43-44-beta-aarch64-wont-boot-on-thinkpad-x13s/183074/13 Link: https://lore.kernel.org/all/20260602081451.3808833-1-zhengxingda@iscas.ac.cn/ Signed-off-by: Icenowy Zheng Signed-off-by: Lain "Fearyncess" Yang --- arch/arm64/boot/dts/qcom/sc8280xp-crd.dts | 3 +++ arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts | 3 +++ arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts | 3 +++ arch/arm64/boot/dts/qcom/sc8280xp-microsoft-arcata.dts | 3 +++ arch/arm64/boot/dts/qcom/sc8280xp-microsoft-blackrock.dts | 3 +++ arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 3 --- 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts index c53e00cae465a6..5fcc1782ab35d8 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts @@ -491,14 +491,17 @@ }; &gpi_dma0 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma1 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma2 { + dma-channel-mask = <0x3>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts index 9819454abe131b..f8171bd292e2af 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts @@ -582,14 +582,17 @@ }; &gpi_dma0 { + dma-channel-mask = <0xb>; status = "okay"; }; &gpi_dma1 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma2 { + dma-channel-mask = <0xb>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts index d84ca010ab9dcb..119eeb1560e160 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts @@ -704,14 +704,17 @@ }; &gpi_dma0 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma1 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma2 { + dma-channel-mask = <0x3>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-arcata.dts b/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-arcata.dts index f2b4470d4407fb..0dfff4854722bc 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-arcata.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-arcata.dts @@ -444,14 +444,17 @@ }; &gpi_dma0 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma1 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma2 { + dma-channel-mask = <0x23>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-blackrock.dts b/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-blackrock.dts index 00bbeeef6f14dd..02075671316bdc 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-blackrock.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-microsoft-blackrock.dts @@ -561,14 +561,17 @@ }; &gpi_dma0 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma1 { + dma-channel-mask = <0x3>; status = "okay"; }; &gpi_dma2 { + dma-channel-mask = <0x23>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index 761f229e8f472c..d4ccca5e293191 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -937,7 +937,6 @@ ; dma-channels = <12>; - dma-channel-mask = <0xfff>; #dma-cells = <3>; iommus = <&apps_smmu 0xb6 0x0>; @@ -1363,7 +1362,6 @@ ; dma-channels = <13>; - dma-channel-mask = <0x1fff>; #dma-cells = <3>; iommus = <&apps_smmu 0x576 0x0>; @@ -1770,7 +1768,6 @@ ; dma-channels = <12>; - dma-channel-mask = <0xfff>; #dma-cells = <3>; iommus = <&apps_smmu 0x96 0x0>; From 8901e116b6eb643bd276ca27a663f8ff40c6de0c Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 2 Jun 2026 15:03:44 +0800 Subject: [PATCH 215/521] FROMLIST: dmaengine: qcom: gpi: set DMA_PRIVATE capability The GPI DMA controller is only responsible for QUP peripherals, and cannot work as a general-purpose DMA accelerator. Set DMA_PRIVATE capability for it. This fixes error messages about GPI being shown when an async-tx consumer is loaded. Fixes: 5d0c3533a19f ("dmaengine: qcom: Add GPI dma driver") Link: https://lore.kernel.org/all/20260602070344.3707256-1-zhengxingda@iscas.ac.cn/ Signed-off-by: Icenowy Zheng Signed-off-by: Lain "Fearyncess" Yang --- drivers/dma/qcom/gpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index c9a6f610ffd9fa..a5055a6273af62 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -2260,6 +2260,7 @@ static int gpi_probe(struct platform_device *pdev) /* clear and Set capabilities */ dma_cap_zero(gpi_dev->dma_device.cap_mask); dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask); + dma_cap_set(DMA_PRIVATE, gpi_dev->dma_device.cap_mask); /* configure dmaengine apis */ gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); From a8904d4360fa6726415e33ada01a2f78900b3924 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 1 Nov 2020 11:33:56 +0800 Subject: [PATCH 216/521] BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support NUMA balancing is available on nearly all architectures, but MIPS lacks it for a long time. In theory, the current NUMA balancing framework only need a "PROTNONE" page table bit and some APIs to manipulate it. So, it is time for us to add MIPS's NUMA balancing support (Only for 64bit now because NUMA balancing depends on huge page implicitly). Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1604201638-4001-1-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflicts in arch/mips/Kconfig, and arch/mips/include/asm/pgtable.h] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 + arch/mips/include/asm/pgtable-64.h | 2 +- arch/mips/include/asm/pgtable-bits.h | 17 +++++++++++++++++ arch/mips/include/asm/pgtable.h | 19 +++++++++++++++++-- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 17d820e8163a18..2bfecccc8f4600 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -19,6 +19,7 @@ config MIPS select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAS_UBSAN select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_SUPPORTS_NUMA_BALANCING if 64BIT select ARCH_KEEP_MEMBLOCK select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF if 64BIT diff --git a/arch/mips/include/asm/pgtable-64.h b/arch/mips/include/asm/pgtable-64.h index 6e854bb11f37de..1f2c3e5d0ad436 100644 --- a/arch/mips/include/asm/pgtable-64.h +++ b/arch/mips/include/asm/pgtable-64.h @@ -260,7 +260,7 @@ static inline int pmd_present(pmd_t pmd) { #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT if (unlikely(pmd_val(pmd) & _PAGE_HUGE)) - return pmd_val(pmd) & _PAGE_PRESENT; + return pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROTNONE); #endif return pmd_val(pmd) != (unsigned long) invalid_pte_table; diff --git a/arch/mips/include/asm/pgtable-bits.h b/arch/mips/include/asm/pgtable-bits.h index 088623ba7b8b17..9ee6e189d329fa 100644 --- a/arch/mips/include/asm/pgtable-bits.h +++ b/arch/mips/include/asm/pgtable-bits.h @@ -52,6 +52,9 @@ enum pgtable_bits { _PAGE_WRITE_SHIFT, _PAGE_ACCESSED_SHIFT, _PAGE_MODIFIED_SHIFT, +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -84,6 +87,9 @@ enum pgtable_bits { _PAGE_WRITE_SHIFT, _PAGE_ACCESSED_SHIFT, _PAGE_MODIFIED_SHIFT, +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -102,6 +108,9 @@ enum pgtable_bits { _PAGE_WRITE_SHIFT, _PAGE_ACCESSED_SHIFT, _PAGE_MODIFIED_SHIFT, +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -131,6 +140,9 @@ enum pgtable_bits { #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) _PAGE_HUGE_SHIFT, #endif +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -158,6 +170,11 @@ enum pgtable_bits { #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) # define _PAGE_HUGE (1 << _PAGE_HUGE_SHIFT) #endif +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) +# define _PAGE_PROTNONE (1 <<_PAGE_PROTNONE_SHIFT) +#else +# define _PAGE_PROTNONE 0 +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) # define _PAGE_SPECIAL (1 << _PAGE_SPECIAL_SHIFT) #else diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h index fa7b935f947ca0..66497e063fce1e 100644 --- a/arch/mips/include/asm/pgtable.h +++ b/arch/mips/include/asm/pgtable.h @@ -25,6 +25,9 @@ struct mm_struct; struct vm_area_struct; +#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_NO_READ | \ + _page_cachable_default) + #define PAGE_SHARED vm_get_page_prot(VM_READ|VM_WRITE|VM_SHARED) #define PAGE_KERNEL __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \ @@ -160,7 +163,7 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt #else #define pte_none(pte) (!(pte_val(pte) & ~_PAGE_GLOBAL)) -#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) +#define pte_present(pte) (pte_val(pte) & (_PAGE_PRESENT | _PAGE_PROTNONE)) #define pte_no_exec(pte) (pte_val(pte) & _PAGE_NO_EXEC) /* @@ -721,7 +724,7 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) static inline pmd_t pmd_mkinvalid(pmd_t pmd) { - pmd_val(pmd) &= ~(_PAGE_PRESENT | _PAGE_VALID | _PAGE_DIRTY); + pmd_val(pmd) &= ~(_PAGE_PRESENT | _PAGE_VALID | _PAGE_PROTNONE | _PAGE_DIRTY); return pmd; } @@ -743,6 +746,18 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +#ifdef CONFIG_NUMA_BALANCING +static inline long pte_protnone(pte_t pte) +{ + return (pte_val(pte) & _PAGE_PROTNONE); +} + +static inline long pmd_protnone(pmd_t pmd) +{ + return (pmd_val(pmd) & _PAGE_PROTNONE); +} +#endif /* CONFIG_NUMA_BALANCING */ + #ifdef _PAGE_HUGE #define pmd_leaf(pmd) ((pmd_val(pmd) & _PAGE_HUGE) != 0) #define pud_leaf(pud) ((pud_val(pud) & _PAGE_HUGE) != 0) From 54bd148542e5635f8a9a600176f5a2ecfdf29bac Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 12 Mar 2020 17:41:23 +0800 Subject: [PATCH 217/521] BACKPORT: FROMLIST: MIPS: Add syscall auditing support The original patch is from Ralf. I have maintained it for more than six years on Loongson platform, and it works perfectly. Most of the commit messages are written by Ralf. MIPS doesn't quite fit into the existing pattern of other architectures and I'd appreciate your comments and maybe even an Acked-by. - Linux on MIPS extends the traditional syscall table used by older UNIX implementations. This is why 32-bit Linux syscalls are starting from 4000; the native 64-bit syscalls start from 5000 and the N32 compat ABI from 6000. The existing syscall bitmap is only large enough for at most 2048 syscalls, so I had to increase AUDIT_BITMASK_SIZE to 256 which provides enough space for 8192 syscalls. Because include/uapi/linux/ audit.h and AUDIT_BITMASK_SIZE are exported to userspace I've used an #ifdef __mips__ for this. - The code treats the little endian MIPS architecture as separate from big endian. Combined with the 3 ABIs that's 6 combinations. I tried to sort of follow the example set by ARM which explicitly lists the (rare) big endian architecture variant - but it doesn't seem to very useful so I wonder if this could be squashed to just the three ABIs without consideration of endianess? Signed-off-by: Ralf Baechle Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1584006083-28936-1-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflicts in arch/mips/Kconfig, and kernel/auditsc.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 13 ++++ arch/mips/include/asm/abi.h | 1 + arch/mips/include/asm/unistd.h | 10 +++ arch/mips/include/uapi/asm/unistd.h | 21 ++++--- arch/mips/kernel/Makefile | 4 ++ arch/mips/kernel/audit-n32.c | 58 +++++++++++++++++ arch/mips/kernel/audit-native.c | 97 +++++++++++++++++++++++++++++ arch/mips/kernel/audit-o32.c | 60 ++++++++++++++++++ arch/mips/kernel/signal.c | 18 ++++++ arch/mips/kernel/signal_n32.c | 8 +++ arch/mips/kernel/signal_o32.c | 8 +++ include/uapi/linux/audit.h | 10 +++ kernel/auditsc.c | 13 ++++ 13 files changed, 312 insertions(+), 9 deletions(-) create mode 100644 arch/mips/kernel/audit-n32.c create mode 100644 arch/mips/kernel/audit-native.c create mode 100644 arch/mips/kernel/audit-o32.c diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 2bfecccc8f4600..3799140b031a7f 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -30,6 +30,7 @@ config MIPS select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WANT_LD_ORPHAN_WARN + select AUDIT_ARCH select BUILDTIME_TABLE_SORT select BUILTIN_DTB_ALL if BUILTIN_DTB select CLONE_BACKWARDS @@ -55,6 +56,7 @@ config MIPS select GENERIC_TIME_VSYSCALL select GUP_GET_PXX_LOW_HIGH if CPU_MIPS32 && PHYS_ADDR_T_64BIT select HAS_IOPORT if !NO_IOPORT_MAP || ISA + select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_COMPILER_H select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_KGDB if MIPS_FP_SUPPORT @@ -1104,6 +1106,15 @@ config FW_ARC config ARCH_MAY_HAVE_PC_FDC bool +config AUDIT_ARCH + bool + +config AUDITSYSCALL_O32 + bool + +config AUDITSYSCALL_N32 + bool + config BOOT_RAW bool @@ -3095,6 +3106,7 @@ config MIPS32_O32 select ARCH_WANT_OLD_COMPAT_IPC select COMPAT select MIPS32_COMPAT + select AUDITSYSCALL_O32 if AUDITSYSCALL help Select this option if you want to run o32 binaries. These are pure 32-bit binaries as used by the 32-bit Linux/MIPS port. Most of @@ -3108,6 +3120,7 @@ config MIPS32_N32 select ARCH_WANT_COMPAT_IPC_PARSE_VERSION select COMPAT select MIPS32_COMPAT + select AUDITSYSCALL_N32 if AUDITSYSCALL help Select this option if you want to run n32 binaries. These are 64-bit binaries using 32-bit quantities for addressing and certain diff --git a/arch/mips/include/asm/abi.h b/arch/mips/include/asm/abi.h index dba7f4b6bebfae..6e717a4a13ceb7 100644 --- a/arch/mips/include/asm/abi.h +++ b/arch/mips/include/asm/abi.h @@ -21,6 +21,7 @@ struct mips_abi { int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig, struct pt_regs *regs, sigset_t *set); const unsigned long restart; + const int audit_arch; unsigned off_sc_fpregs; unsigned off_sc_fpc_csr; diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h index 6a974b990f4b21..91d0c7fd6d4b05 100644 --- a/arch/mips/include/asm/unistd.h +++ b/arch/mips/include/asm/unistd.h @@ -64,4 +64,14 @@ #endif /* !__ASSEMBLER__ */ +#ifdef CONFIG_MIPS32_N32 +#define NR_syscalls (__NR_N32_Linux + __NR_N32_Linux_syscalls) +#elif defined(CONFIG_64BIT) +#define NR_syscalls (__NR_64_Linux + __NR_64_Linux_syscalls) +#elif defined(CONFIG_32BIT) +#define NR_syscalls (__NR_O32_Linux + __NR_O32_Linux_syscalls) +#else +#error Must know ABIs in use to define NR_syscalls +#endif + #endif /* _ASM_UNISTD_H */ diff --git a/arch/mips/include/uapi/asm/unistd.h b/arch/mips/include/uapi/asm/unistd.h index 4abe387549ad25..b501ea16ccd403 100644 --- a/arch/mips/include/uapi/asm/unistd.h +++ b/arch/mips/include/uapi/asm/unistd.h @@ -6,34 +6,37 @@ * * Copyright (C) 1995, 96, 97, 98, 99, 2000 by Ralf Baechle * Copyright (C) 1999, 2000 Silicon Graphics, Inc. - * - * Changed system calls macros _syscall5 - _syscall7 to push args 5 to 7 onto - * the stack. Robin Farine for ACN S.A, Copyright (C) 1996 by ACN S.A */ #ifndef _UAPI_ASM_UNISTD_H #define _UAPI_ASM_UNISTD_H #include -#if _MIPS_SIM == _MIPS_SIM_ABI32 +#if (defined(__WANT_SYSCALL_NUMBERS) && \ + (__WANT_SYSCALL_NUMBERS == _MIPS_SIM_ABI32)) || \ + (!defined(__WANT_SYSCALL_NUMBERS) && _MIPS_SIM == _MIPS_SIM_ABI32) #define __NR_Linux 4000 #include -#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ +#endif /* Want O32 || _MIPS_SIM == _MIPS_SIM_ABI32 */ -#if _MIPS_SIM == _MIPS_SIM_ABI64 +#if (defined(__WANT_SYSCALL_NUMBERS) && \ + (__WANT_SYSCALL_NUMBERS == _MIPS_SIM_ABI64)) || \ + (!defined(__WANT_SYSCALL_NUMBERS) && _MIPS_SIM == _MIPS_SIM_ABI64) #define __NR_Linux 5000 #include -#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ +#endif /* Want N64 || _MIPS_SIM == _MIPS_SIM_ABI64 */ -#if _MIPS_SIM == _MIPS_SIM_NABI32 +#if (defined(__WANT_SYSCALL_NUMBERS) && \ + (__WANT_SYSCALL_NUMBERS == _MIPS_SIM_NABI32)) || \ + (!defined(__WANT_SYSCALL_NUMBERS) && _MIPS_SIM == _MIPS_SIM_NABI32) #define __NR_Linux 6000 #include -#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ +#endif /* Want N32 || _MIPS_SIM == _MIPS_SIM_NABI32 */ #endif /* _UAPI_ASM_UNISTD_H */ diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 95a1e674fd6785..b9786a289592cd 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -105,6 +105,10 @@ obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_mipsxx.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_UPROBES) += uprobes.o +obj-$(CONFIG_AUDITSYSCALL_O32) += audit-o32.o +obj-$(CONFIG_AUDITSYSCALL_N32) += audit-n32.o +obj-$(CONFIG_AUDITSYSCALL) += audit-native.o + obj-$(CONFIG_MIPS_CM) += mips-cm.o obj-$(CONFIG_MIPS_CPC) += mips-cpc.o diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c new file mode 100644 index 00000000000000..2248badc3acb65 --- /dev/null +++ b/arch/mips/kernel/audit-n32.c @@ -0,0 +1,58 @@ +#define __WANT_SYSCALL_NUMBERS _MIPS_SIM_NABI32 + +#include +#include +#include +#include + +static unsigned dir_class_n32[] = { +#include +~0U +}; + +static unsigned read_class_n32[] = { +#include +~0U +}; + +static unsigned write_class_n32[] = { +#include +~0U +}; + +static unsigned chattr_class_n32[] = { +#include +~0U +}; + +static unsigned signal_class_n32[] = { +#include +~0U +}; + +int audit_classify_syscall_n32(int abi, unsigned syscall) +{ + switch (syscall) { + case __NR_open: + return 2; + case __NR_openat: + return 3; + case __NR_execve: + return 5; + default: + return 0; + } +} + +static int __init audit_classes_n32_init(void) +{ + audit_register_class(AUDIT_CLASS_WRITE_N32, write_class_n32); + audit_register_class(AUDIT_CLASS_READ_N32, read_class_n32); + audit_register_class(AUDIT_CLASS_DIR_WRITE_N32, dir_class_n32); + audit_register_class(AUDIT_CLASS_CHATTR_N32, chattr_class_n32); + audit_register_class(AUDIT_CLASS_SIGNAL_N32, signal_class_n32); + + return 0; +} + +__initcall(audit_classes_n32_init); diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c new file mode 100644 index 00000000000000..09ae3dbcfecbcc --- /dev/null +++ b/arch/mips/kernel/audit-native.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +static unsigned dir_class[] = { +#include +~0U +}; + +static unsigned read_class[] = { +#include +~0U +}; + +static unsigned write_class[] = { +#include +~0U +}; + +static unsigned chattr_class[] = { +#include +~0U +}; + +static unsigned signal_class[] = { +#include +~0U +}; + + +/* + * Pretend to be a single architecture + */ +int audit_classify_arch(int arch) +{ + return 0; +} + +extern int audit_classify_syscall_o32(int abi, unsigned syscall); +extern int audit_classify_syscall_n32(int abi, unsigned syscall); + +int audit_classify_syscall(int abi, unsigned syscall) +{ + int res; + + switch (syscall) { + case __NR_open: + res = 2; + break; + + case __NR_openat: + res = 3; + break; + +#ifdef __NR_socketcall /* Only exists on O32 */ + case __NR_socketcall: + res = 4; + break; +#endif + case __NR_execve: + res = 5; + break; + default: +#ifdef CONFIG_AUDITSYSCALL_O32 + res = audit_classify_syscall_o32(abi, syscall); + if (res) + break; +#endif +#ifdef CONFIG_AUDITSYSCALL_N32 + res = audit_classify_syscall_n32(abi, syscall); + if (res) + break; +#endif + if (abi == AUDIT_ARCH_MIPS || abi == AUDIT_ARCH_MIPSEL) + res = 1; + else if (abi == AUDIT_ARCH_MIPS64 || abi == AUDIT_ARCH_MIPSEL64) + res = 0; + else if (abi == AUDIT_ARCH_MIPS64N32 || abi == AUDIT_ARCH_MIPSEL64N32) + res = 6; + } + + return res; +} + +static int __init audit_classes_init(void) +{ + audit_register_class(AUDIT_CLASS_WRITE, write_class); + audit_register_class(AUDIT_CLASS_READ, read_class); + audit_register_class(AUDIT_CLASS_DIR_WRITE, dir_class); + audit_register_class(AUDIT_CLASS_CHATTR, chattr_class); + audit_register_class(AUDIT_CLASS_SIGNAL, signal_class); + + return 0; +} + +__initcall(audit_classes_init); diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c new file mode 100644 index 00000000000000..e8b9b50f7d2671 --- /dev/null +++ b/arch/mips/kernel/audit-o32.c @@ -0,0 +1,60 @@ +#define __WANT_SYSCALL_NUMBERS _MIPS_SIM_ABI32 + +#include +#include +#include +#include + +static unsigned dir_class_o32[] = { +#include +~0U +}; + +static unsigned read_class_o32[] = { +#include +~0U +}; + +static unsigned write_class_o32[] = { +#include +~0U +}; + +static unsigned chattr_class_o32[] = { +#include +~0U +}; + +static unsigned signal_class_o32[] = { +#include +~0U +}; + +int audit_classify_syscall_o32(int abi, unsigned syscall) +{ + switch (syscall) { + case __NR_open: + return 2; + case __NR_openat: + return 3; + case __NR_socketcall: + return 4; + case __NR_execve: + return 5; + default: + return 0; + } +} + +static int __init audit_classes_o32_init(void) +{ + audit_register_class(AUDIT_CLASS_WRITE_32, write_class_o32); + audit_register_class(AUDIT_CLASS_READ_32, read_class_o32); + audit_register_class(AUDIT_CLASS_DIR_WRITE_32, dir_class_o32); + audit_register_class(AUDIT_CLASS_CHATTR_32, chattr_class_o32); + audit_register_class(AUDIT_CLASS_SIGNAL_32, signal_class_o32); + + return 0; +} + +__initcall(audit_classes_o32_init); diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 4a10f18a880604..c803c39f5cbe50 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -8,6 +8,7 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * Copyright (C) 2014, Imagination Technologies Ltd. */ +#include #include #include #include @@ -805,6 +806,23 @@ struct mips_abi mips_abi = { #endif .setup_rt_frame = setup_rt_frame, .restart = __NR_restart_syscall, +#ifdef CONFIG_64BIT +# ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS64, +# elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL64, +# else +# error "Neither big nor little endian ???" +# endif +#else +# ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS, +# elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL, +# else +# error "Neither big nor little endian ???" +# endif +#endif .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), diff --git a/arch/mips/kernel/signal_n32.c b/arch/mips/kernel/signal_n32.c index 139d2596b0d409..fb9ddefae9648c 100644 --- a/arch/mips/kernel/signal_n32.c +++ b/arch/mips/kernel/signal_n32.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2003 Broadcom Corporation */ +#include #include #include #include @@ -137,6 +138,13 @@ static int setup_rt_frame_n32(void *sig_return, struct ksignal *ksig, struct mips_abi mips_abi_n32 = { .setup_rt_frame = setup_rt_frame_n32, .restart = __NR_N32_restart_syscall, +#ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS64N32, +#elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL64N32, +#else +# error "Neither big nor little endian ???" +#endif .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), diff --git a/arch/mips/kernel/signal_o32.c b/arch/mips/kernel/signal_o32.c index 4f04584596507a..ea3f280a18aa0d 100644 --- a/arch/mips/kernel/signal_o32.c +++ b/arch/mips/kernel/signal_o32.c @@ -8,6 +8,7 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * Copyright (C) 2016, Imagination Technologies Ltd. */ +#include #include #include #include @@ -245,6 +246,13 @@ struct mips_abi mips_abi_32 = { .setup_frame = setup_frame_32, .setup_rt_frame = setup_rt_frame_32, .restart = __NR_O32_restart_syscall, +#ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS, +#elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL, +#else +# error "Neither big nor little endian ???" +#endif .off_sc_fpregs = offsetof(struct sigcontext32, sc_fpregs), .off_sc_fpc_csr = offsetof(struct sigcontext32, sc_fpc_csr), diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index e8f5ce677df734..c2e65021467fd2 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -193,7 +193,11 @@ * AUDIT_LIST commands must be implemented. */ #define AUDIT_MAX_FIELDS 64 #define AUDIT_MAX_KEY_LEN 256 +#ifdef __mips__ +#define AUDIT_BITMASK_SIZE 256 +#else #define AUDIT_BITMASK_SIZE 64 +#endif #define AUDIT_WORD(nr) ((__u32)((nr)/32)) #define AUDIT_BIT(nr) (1U << ((nr) - AUDIT_WORD(nr)*32)) @@ -209,6 +213,12 @@ #define AUDIT_CLASS_SIGNAL 8 #define AUDIT_CLASS_SIGNAL_32 9 +#define AUDIT_CLASS_DIR_WRITE_N32 10 +#define AUDIT_CLASS_CHATTR_N32 11 +#define AUDIT_CLASS_READ_N32 12 +#define AUDIT_CLASS_WRITE_N32 13 +#define AUDIT_CLASS_SIGNAL_N32 14 + /* This bitmask is used to validate user input. It represents all bits that * are currently used in an audit field constant understood by the kernel. * If you are adding a new #define AUDIT_, please ensure that diff --git a/kernel/auditsc.c b/kernel/auditsc.c index abdf8da3be9340..2971f0d2574ac3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -189,6 +189,19 @@ static int audit_match_perm(struct audit_context *ctx, int mask) return mask & AUDIT_PERM_EXEC; case AUDITSC_OPENAT2: return mask & ACC_MODE((u32)ctx->openat2.flags); +#ifdef CONFIG_MIPS + case 6: /* for N32 */ + if ((mask & AUDIT_PERM_WRITE) && + audit_match_class(AUDIT_CLASS_WRITE_N32, n)) + return 1; + if ((mask & AUDIT_PERM_READ) && + audit_match_class(AUDIT_CLASS_READ_N32, n)) + return 1; + if ((mask & AUDIT_PERM_ATTR) && + audit_match_class(AUDIT_CLASS_CHATTR_N32, n)) + return 1; + return 0; +#endif default: return 0; } From 108e982330ee40138ab2f33692a9790d903e19ec Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 3 Jun 2026 10:34:30 +0800 Subject: [PATCH 218/521] FROMLIST: LoongArch: KVM: Fix FPU register width issue with user access API At the beginning, only 64 bit FPU is supported. With FPU register get interface, 64 bit FPU data is copied to user space, the same with FPU set API. However with LSX and LASX supported in later, there should be FPU data copied with bigger width. Here fixes this issue, copy the whole 256 bit FPU data to user space. Fixes: db1ecca22edf ("LoongArch: KVM: Add LSX (128bit SIMD) support") Cc: stable@vger.kernel.org Signed-off-by: Bibo Mao Link: https://lore.kernel.org/loongarch/20260603023430.1748197-1-maobibo@loongson.cn/ Signed-off-by: Mingcong Bai --- arch/loongarch/kvm/vcpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index e28084c49e6820..2e40386f868694 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1312,7 +1312,7 @@ int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) fpu->fcc = vcpu->arch.fpu.fcc; fpu->fcsr = vcpu->arch.fpu.fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], FPU_REG_WIDTH / 64); + memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], sizeof(union fpureg)); return 0; } @@ -1324,7 +1324,7 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) vcpu->arch.fpu.fcc = fpu->fcc; vcpu->arch.fpu.fcsr = fpu->fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], FPU_REG_WIDTH / 64); + memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], sizeof(union fpureg)); return 0; } From d5b1ce8e3d5027d75897a6032dcd9dad1b9121ca Mon Sep 17 00:00:00 2001 From: ChunHao Lin Date: Fri, 11 Jul 2025 11:44:12 +0800 Subject: [PATCH 219/521] FROMLIST: r8169: add quirk for RTL8116af SerDes RTL8116af is a variation of RTL8168fp. It uses SerDes instead of PHY. But SerDes status will not reflect to PHY. So it needs quirk to help to reflect SerDes status during PHY read. Signed-off-by: ChunHao Lin Link: https://lore.kernel.org/lkml/20250711034412.17937-1-hau@realtek.com/ Signed-off-by: Mingcong Bai --- drivers/net/ethernet/realtek/r8169_main.c | 40 ++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 791277e750bad4..41f03d972edda8 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -1273,6 +1273,34 @@ static int r8168g_mdio_read(struct rtl8169_private *tp, int reg) return r8168_phy_ocp_read(tp, tp->ocp_base + reg * 2); } +/* The quirk reflects RTL8116af SerDes status. */ +static int r8116af_mdio_read_quirk(struct rtl8169_private *tp, int reg) +{ + u8 phyStatus = RTL_R8(tp, PHYstatus); + + if (!(phyStatus & LinkStatus)) + return 0; + + /* BMSR */ + if (tp->ocp_base == OCP_STD_PHY_BASE && reg == MII_BMSR) + return BMSR_ANEGCOMPLETE | BMSR_LSTATUS; + + /* PHYSR */ + if (tp->ocp_base == 0xa430 && reg == 0x12) + { if (phyStatus & _1000bpsF) + return 0x0028; + else if (phyStatus & _100bps) + return 0x0018; + } + + return 0; +} + +static int r8116af_mdio_read(struct rtl8169_private *tp, int reg) +{ + return r8168g_mdio_read(tp, reg) | r8116af_mdio_read_quirk(tp, reg); +} + static void mac_mcu_write(struct rtl8169_private *tp, int reg, int value) { if (reg == 0x1f) { @@ -1366,6 +1394,13 @@ static int r8168dp_2_mdio_read(struct rtl8169_private *tp, int reg) return value; } +static bool rtl_is_8116af(struct rtl8169_private *tp) +{ + return tp->mac_version == RTL_GIGA_MAC_VER_52 && + (r8168_mac_ocp_read(tp, 0xdc00) & 0x0078) == 0x0030 && + (r8168_mac_ocp_read(tp, 0xd006) & 0x00ff) == 0x0000; +} + static void rtl_writephy(struct rtl8169_private *tp, int location, int val) { switch (tp->mac_version) { @@ -1389,7 +1424,10 @@ static int rtl_readphy(struct rtl8169_private *tp, int location) case RTL_GIGA_MAC_VER_31: return r8168dp_2_mdio_read(tp, location); case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: - return r8168g_mdio_read(tp, location); + if (rtl_is_8116af(tp)) + return r8116af_mdio_read(tp, location); + else + return r8168g_mdio_read(tp, location); default: return r8169_mdio_read(tp, location); } From 5c94164314378467939258af5facb71073ef5bb2 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Wed, 24 Jun 2026 01:36:48 +0800 Subject: [PATCH 220/521] FROMLIST: pci: loongson: Avoid L0s on LS7A1000 PCIe x8 [0014:7a29] Root Ports rev2 Commit f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms") has broke booting of a Loongson 3B4000 + 7A1000 server with an Intel 750 Series SSD. We also found a Loongson 3A5000 board using the same 7A1000 bridge chip exhibiting the same issue with pcie_aspm=force and pcie_aspm.policy=powersave (ASPM is not enabled by default on the 3A5000 board because it's based on ACPI and the _OSC method has not been implemented in its DSDT/SSDT yet). This seems only affecting the 7A1000 chips shipping the revision 2 of the PCIe x8 Root Port: on other two boards with a (persumably older) LS7A1000 bridge chip utilizing the revision 1 of the root port, the issue does not reproduce. Cc: Mingcong Bai Cc: Henry Chen Cc: Han Gao Cc: Jiaxun Yang Fixes: f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms") Signed-off-by: Xi Ruoyao Link: https://lore.kernel.org/all/20260623173648.222922-1-xry111@xry111.site/ Signed-off-by: Mingcong Bai --- drivers/pci/controller/pci-loongson.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 9609e6f50b9816..be588feb28e4ba 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -212,6 +212,19 @@ static void loongson_pci_bridge_speed_quirk(struct pci_dev *pdev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c19, loongson_pci_bridge_speed_quirk); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c29, loongson_pci_bridge_speed_quirk); +/* + * Some devices, for example Intel 750 Series SSD, experiences data loss + * (read timeout) on LS7A1000 PCIe x8 Root Port when ASPM L0s is enabled. + * This seems only affecting the revision 2. + */ +static void loongson_port2_aspm_quirk(struct pci_dev *pdev) +{ + if (pdev->revision == 2) + pcie_aspm_remove_cap(pdev, PCI_EXP_LNKCAP_ASPM_L0S); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT2, + loongson_port2_aspm_quirk); + static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; From 31f65f2ca3c972f77720380adcd2f97828465320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E8=87=B4=E9=82=A6=20=28XIE=20Zhibang=29?= Date: Tue, 30 Jun 2026 05:04:21 +0000 Subject: [PATCH 221/521] FROMLIST: HID: i2c-hid: Refactor _DSM helper and add i2c-hid-acpi-prp0001 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the _DSM call that gets the HID descriptor address from i2c-hid-acpi.c into i2c-hid-acpi.h as a static inline so both the ACPI and the new PRP0001 driver can use it. While refactoring, move the blacklist check and the _DSM call to the top of probe() to avoid a pointless alloc when the device is blacklisted or does not implement the _DSM. Some devices, for example the Lenovo KaiTian N60d and Inspur CP300L3, are declared with _HID "PRP0001" and _DSD compatible "hid-over-i2c" but lack "hid-descr-addr" from the _DSD and provide the HID descriptor address only through an ACPI _DSM. The OF driver fails to probe them because it requires hid-descr-addr. Add a new driver that handles these devices by calling the shared _DSM helper. Link: https://lore.kernel.org/tencent_F6FC553D1BB737FC00062AD0FEF43C580F0A@qq.com Fixes: b33752c30023 ("HID: i2c-hid: Reorganize so ACPI and OF are separate modules") Signed-off-by: 谢致邦 (XIE Zhibang) Link: https://lore.kernel.org/all/tencent_76FCB13C1FB6FBFFA004F2D19AF6E458030A@qq.com/ Signed-off-by: Mingcong Bai --- drivers/hid/i2c-hid/Makefile | 2 +- drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c | 104 +++++++++++++++++++++ drivers/hid/i2c-hid/i2c-hid-acpi.c | 52 +++-------- drivers/hid/i2c-hid/i2c-hid-acpi.h | 33 +++++++ 4 files changed, 152 insertions(+), 39 deletions(-) create mode 100644 drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c create mode 100644 drivers/hid/i2c-hid/i2c-hid-acpi.h diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile index 55bd5e0f35af32..38d5d827f3ce4f 100644 --- a/drivers/hid/i2c-hid/Makefile +++ b/drivers/hid/i2c-hid/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_I2C_HID_CORE) += i2c-hid.o i2c-hid-objs = i2c-hid-core.o i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o -obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o +obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o i2c-hid-acpi-prp0001.o obj-$(CONFIG_I2C_HID_OF) += i2c-hid-of.o obj-$(CONFIG_I2C_HID_OF_ELAN) += i2c-hid-of-elan.o obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c new file mode 100644 index 00000000000000..d2cf4714ae7f1c --- /dev/null +++ b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HID over I2C driver for PRP0001 devices missing hid-descr-addr + * + * Some devices, for example the Lenovo KaiTian N60d and Inspur CP300L3, use + * _HID "PRP0001" with _DSD compatible "hid-over-i2c" but lack "hid-descr-addr" + * from the _DSD. The HID descriptor address is provided only through an ACPI + * _DSM. The TPD0 node in the DSDT shows _DSM Function 1 returning 0x20. + * + * Copyright (C) 2026 谢致邦 (XIE Zhibang) + */ + +#include +#include +#include +#include +#include + +#include "i2c-hid.h" +#include "i2c-hid-acpi.h" + +static int i2c_hid_acpi_prp0001_power_up(struct i2chid_ops *ops) +{ + /* give the device time to power up */ + msleep(750); + return 0; +} + +static struct i2chid_ops i2c_hid_acpi_prp0001_ops = { + .power_up = i2c_hid_acpi_prp0001_power_up, + /* + * No .restore_sequence needed: the _DSM on these devices returns a + * constant (0x20) with no side effects, unlike some PNP0C50 _DSM + * implementations that switch the hardware between PS/2 and I2C modes. + */ +}; + +static int i2c_hid_acpi_prp0001_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct acpi_device *adev; + u16 hid_descriptor_address; + int ret; + + /* If hid-descr-addr is present, let i2c-hid-of handle it */ + if (device_property_present(dev, "hid-descr-addr")) + return -ENODEV; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + ret = i2c_hid_acpi_get_descriptor(adev); + if (ret < 0) + return ret; + dev_warn(dev, + "hid-descr-addr device property NOT found, using ACPI _DSM fallback. Contact vendor for firmware update!\n"); + hid_descriptor_address = ret; + + /* + * No acpi_device_fix_up_power() needed: TPD0 has no _PS0, _PS3, _PSC + * or _PRx methods and follows I2C bus power. + */ + return i2c_hid_core_probe(client, &i2c_hid_acpi_prp0001_ops, + hid_descriptor_address, 0); +} + +static const struct of_device_id i2c_hid_acpi_prp0001_of_match[] = { + { .compatible = "hid-over-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_hid_acpi_prp0001_of_match); + +static const struct i2c_device_id i2c_hid_acpi_prp0001_id[] = { + { .name = "hid-over-i2c" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, i2c_hid_acpi_prp0001_id); + +static struct i2c_driver i2c_hid_acpi_prp0001_driver = { + .driver = { + .name = "i2c_hid_acpi_prp0001", + .pm = &i2c_hid_core_pm, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + /* + * of_match_ptr() makes this NULL when CONFIG_OF=n, but that's + * fine: the I2C id_table with "hid-over-i2c" handles matching + * via client->name (set by acpi_set_modalias() from the _DSD + * compatible property). + */ + .of_match_table = of_match_ptr(i2c_hid_acpi_prp0001_of_match), + }, + + .probe = i2c_hid_acpi_prp0001_probe, + .remove = i2c_hid_core_remove, + .shutdown = i2c_hid_core_shutdown, + .id_table = i2c_hid_acpi_prp0001_id, +}; + +module_i2c_driver(i2c_hid_acpi_prp0001_driver); + +MODULE_DESCRIPTION("HID over I2C driver for PRP0001 devices missing hid-descr-addr"); +MODULE_AUTHOR("谢致邦 (XIE Zhibang) "); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c index abd700a101f46c..13f977d6aab615 100644 --- a/drivers/hid/i2c-hid/i2c-hid-acpi.c +++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c @@ -25,9 +25,9 @@ #include #include #include -#include #include "i2c-hid.h" +#include "i2c-hid-acpi.h" struct i2c_hid_acpi { struct i2chid_ops ops; @@ -48,39 +48,11 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = { { } }; -/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */ -static guid_t i2c_hid_guid = - GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555, - 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE); - -static int i2c_hid_acpi_get_descriptor(struct i2c_hid_acpi *ihid_acpi) -{ - struct acpi_device *adev = ihid_acpi->adev; - acpi_handle handle = acpi_device_handle(adev); - union acpi_object *obj; - u16 hid_descriptor_address; - - if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0) - return -ENODEV; - - obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL, - ACPI_TYPE_INTEGER); - if (!obj) { - acpi_handle_err(handle, "Error _DSM call to get HID descriptor address failed\n"); - return -ENODEV; - } - - hid_descriptor_address = obj->integer.value; - ACPI_FREE(obj); - - return hid_descriptor_address; -} - static void i2c_hid_acpi_restore_sequence(struct i2chid_ops *ops) { struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops); - i2c_hid_acpi_get_descriptor(ihid_acpi); + i2c_hid_acpi_get_descriptor(ihid_acpi->adev); } static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops) @@ -93,24 +65,28 @@ static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops) static int i2c_hid_acpi_probe(struct i2c_client *client) { struct device *dev = &client->dev; + struct acpi_device *adev = ACPI_COMPANION(dev); struct i2c_hid_acpi *ihid_acpi; u16 hid_descriptor_address; int ret; - ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL); + if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0) + return -ENODEV; + + ret = i2c_hid_acpi_get_descriptor(adev); + if (ret < 0) + return ret; + hid_descriptor_address = ret; + + ihid_acpi = devm_kzalloc(dev, sizeof(*ihid_acpi), GFP_KERNEL); if (!ihid_acpi) return -ENOMEM; - ihid_acpi->adev = ACPI_COMPANION(dev); + ihid_acpi->adev = adev; ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail; ihid_acpi->ops.restore_sequence = i2c_hid_acpi_restore_sequence; - ret = i2c_hid_acpi_get_descriptor(ihid_acpi); - if (ret < 0) - return ret; - hid_descriptor_address = ret; - - acpi_device_fix_up_power(ihid_acpi->adev); + acpi_device_fix_up_power(adev); return i2c_hid_core_probe(client, &ihid_acpi->ops, hid_descriptor_address, 0); diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.h b/drivers/hid/i2c-hid/i2c-hid-acpi.h new file mode 100644 index 00000000000000..0bbed1853313dc --- /dev/null +++ b/drivers/hid/i2c-hid/i2c-hid-acpi.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _I2C_HID_ACPI_H +#define _I2C_HID_ACPI_H + +#include +#include + +static inline int i2c_hid_acpi_get_descriptor(struct acpi_device *adev) +{ + /* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */ + static const guid_t i2c_hid_guid = + GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555, + 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE); + + acpi_handle handle = acpi_device_handle(adev); + union acpi_object *obj; + u16 addr; + + obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, + 1, 1, NULL, ACPI_TYPE_INTEGER); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call to get HID descriptor address failed\n"); + return -ENODEV; + } + + addr = obj->integer.value; + ACPI_FREE(obj); + return addr; +} + +#endif From f820e4b43a2fabd0e8a712d123be8cc737310afb Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 30 Jun 2026 11:01:40 +0800 Subject: [PATCH 222/521] FROMLIST: can: Add Loongson CAN-FD controller driver Add support for the CAN-FD controller integrated into Loongson-2K series SoCs. The controller supports both Classic CAN and CAN FD, with up to 8 transmit buffers, hardware timestamping, error counters, and various control modes (loopback, listen-only, one-shot, non-ISO FD, etc.). The driver implements: - NAPI for RX path - TX buffer management with echo skb support - Bus error reporting and fault confinement state handling - Bit timing configuration for nominal and data phase - Secondary Sample Point (SSP) configuration for high bitrates Co-developed-by: Bingxiong Li Signed-off-by: Bingxiong Li Signed-off-by: Binbin Zhou Link: https://lore.kernel.org/loongarch/cover.1782787997.git.zhoubinbin@loongson.cn/ Signed-off-by: Mingcong Bai --- MAINTAINERS | 7 + drivers/net/can/Kconfig | 11 + drivers/net/can/Makefile | 1 + drivers/net/can/loongson_canfd.c | 1693 ++++++++++++++++++++++++++++++ 4 files changed, 1712 insertions(+) create mode 100644 drivers/net/can/loongson_canfd.c diff --git a/MAINTAINERS b/MAINTAINERS index 7609c81f7841e5..fcd8e3aee952c0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15077,6 +15077,13 @@ F: arch/loongarch/ F: drivers/*/*loongarch* F: drivers/cpufreq/loongson3_cpufreq.c +LOONGSON CAN FD DRIVER +M: Bingxiong Li +M: Binbin Zhou +L: linux-can@vger.kernel.org +S: Maintained +F: drivers/net/can/loongson_canfd/ + LOONGSON GPIO DRIVER M: Yinbo Zhu L: linux-gpio@vger.kernel.org diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index e15e320db47636..55c40b0654fbe8 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -189,6 +189,17 @@ config CAN_KVASER_PCIEFD Kvaser M.2 PCIe 4xCAN Kvaser PCIe 8xCAN +config CAN_LOONGSON_CANFD + tristate "Loongson CAN-FD controller" + depends on HAS_IOMEM + select REGMAP_MMIO + help + This is a canfd driver switch for the Loongson platform, + integrated with the Loongson-2K series SoCs. + + To compile as a module, choose M here: the module will be + called loongson_canfd. + config CAN_SLCAN tristate "Serial / USB serial CAN Adaptors (slcan)" depends on TTY diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index d7bc10a6b8eae2..1ce78443d7102b 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CAN_GRCAN) += grcan.o obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd/ +obj-$(CONFIG_CAN_LOONGSON_CANFD) += loongson_canfd.o obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_M_CAN) += m_can/ obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/ diff --git a/drivers/net/can/loongson_canfd.c b/drivers/net/can/loongson_canfd.c new file mode 100644 index 00000000000000..1c7381129c2b61 --- /dev/null +++ b/drivers/net/can/loongson_canfd.c @@ -0,0 +1,1693 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * LOONGSON CANFD controller + * + * Copyright (C) 2024-2026 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOONGSON_CANFD_DEVICE_ID 0x0 /* CANFD controller ID Register */ +#define LOONGSON_CANFD_MODE 0x4 /* Mode Configuration Register */ +#define LOONGSON_CANFD_CONF 0x8 /* Configure Register */ +#define LOONGSON_CANFD_STAT 0xc /* Status Register */ +#define LOONGSON_CANFD_CMD 0x10 /* Command Register */ +#define LOONGSON_CANFD_INT_STAT 0x14 /* Interrupt Status Register */ +#define LOONGSON_CANFD_INT_ENA 0x18 /* Interrupt Enable Register */ +#define LOONGSON_CANFD_INT_MASK 0x1c /* Interrupt Mask Register */ +#define LOONGSON_CANFD_BTR_NORM 0x20 /* Normal Rate Configuration Register */ +#define LOONGSON_CANFD_BTR_FD 0x24 /* FD Data Rate Configuration Register */ +#define LOONGSON_CANFD_ERL 0x28 /* Error Threshold Configuration Register */ +#define LOONGSON_CANFD_FSTAT 0x2c /* Error Status Register */ +#define LOONGSON_CANFD_ERC 0x30 /* Error Count Register */ +#define LOONGSON_CANFD_BRE 0x34 /* Rate Error Count Register */ +#define LOONGSON_CANFD_CTR_PRES 0x38 /* Error Count Debug Register */ +#define LOONGSON_CANFD_ERR_CAPT 0x3c /* Error Capture Status Register */ +#define LOONGSON_CANFD_RETX_CNT 0x40 /* Retransmission Count Register */ +#define LOONGSON_CANFD_ALC 0x44 /* Lost Arbitration Capture Register */ +#define LOONGSON_CANFD_TRV_DLY 0x48 /* Transmission Delay Measurement Register */ +#define LOONGSON_CANFD_SSP_CFG 0x4c /* Second Sampling Point Configuration Register */ +#define LOONGSON_CANFD_RX_FR_CNT 0x50 /* Receive Message Count Register */ +#define LOONGSON_CANFD_TX_FR_CNT 0x54 /* Transmit Message Count Register */ +#define LOONGSON_CANFD_DEBUG 0x58 /* Debug Register */ +#define LOONGSON_CANFD_TS 0x5c /* Timestamp Register */ +#define LOONGSON_CANFD_TX_FRM_TST 0x60 /* Transmit Message Debug Register */ +#define LOONGSON_CANFD_FRC_DIV 0x64 /* Fractional Divider Ratio Register */ +#define LOONGSON_CANFD_FLT_A_MASK 0x68 /* Filter A Mask Register */ +#define LOONGSON_CANFD_FLT_A_VAL 0x6c /* Filter A value Register */ +#define LOONGSON_CANFD_FLT_B_MASK 0x70 /* Filter B Mask Register */ +#define LOONGSON_CANFD_FLT_B_VAL 0x74 /* Filter B value Register */ +#define LOONGSON_CANFD_FLT_C_MASK 0x78 /* Filter C Mask Register */ +#define LOONGSON_CANFD_FLT_C_VAL 0x7c /* Filter C value Register */ +#define LOONGSON_CANFD_FLT_R_LOW 0x80 /* Range Filter Low Threshold Register */ +#define LOONGSON_CANFD_FLT_R_HI 0x84 /* Range Filter High Threshold Register */ +#define LOONGSON_CANFD_FLT_CTRL 0x88 /* Filter Control Register */ +#define LOONGSON_CANFD_RX_MEM_INFO 0x8c /* Receive Buffer Information Register */ +#define LOONGSON_CANFD_RX_PRT 0x90 /* Receive Buffer Pointer Register */ +#define LOONGSON_CANFD_RX_STAT 0x94 /* Receive Buffer Status Register */ +#define LOONGSON_CANFD_RX_DATA 0x98 /* Receive Data Register */ +#define LOONGSON_CANFD_TX_STAT 0x9c /* Transmit Buffer Status Register */ +#define LOONGSON_CANFD_TX_CMD 0xa0 /* Transmit Command Register */ +#define LOONGSON_CANFD_TX_SEL 0xa4 /* Transmit Buffer Selection Register */ +#define LOONGSON_CANFD_TX_DATA_1 0xb0 +#define LOONGSON_CANFD_TX_DATA_18 0xf4 + +/* Bitfields of CANFD controller ID register */ +#define REG_ID_MASK GENMASK(15, 0) +#define REG_ID_VER_MIN GENMASK(23, 16) +#define REG_ID_VER_MAJ GENMASK(31, 24) + +/* Bitfields of Mode Configuration register */ +#define REG_MODE_RST BIT(0) /* Soft Reset */ +#define REG_MODE_BMM BIT(1) /* Bus Monitoring Mode */ +#define REG_MODE_STM BIT(2) /* Self-Test Mode */ +#define REG_MODE_AFM BIT(3) /* Receive Filter Mode */ +#define REG_MODE_FDE BIT(4) /* CANFD Enabled */ +#define REG_MODE_TTTM BIT(5) /* Timed Transmission Mode */ +#define REG_MODE_ROM BIT(6) /* Restricted Operation Mode */ +#define REG_MODE_ACF BIT(7) /* No ACK Mode*/ +#define REG_MODE_TSTM BIT(8) /* Test Mode */ +#define REG_MODE_RXBAM BIT(9) /* Receive Buffer Auto Mode */ +#define REG_MODE_ITSM BIT(10) /* Internal Timestamp Mode */ +#define REG_MODE_RTSOP BIT(12) /* Receive Buffer Timestamp Addition Mode */ +#define REG_MODE_BUFM BIT(13) /* Transmit Buffer Mode */ + +/* Bitfields of Configure register */ +#define REG_CONF_RTRLE BIT(0) /* Retransmission Threshold Enable */ +#define REG_CONF_RTRTH GENMASK(4, 1) /* Retransmission Threshold */ +#define REG_CONF_ILBP BIT(5) /* Internal Loopback Mode */ +#define REG_CONF_ENA BIT(6) /* Controller Enable */ +#define REG_CONF_NISOFD BIT(7) /* NON-ISO CANFD Protocol Enable */ +#define REG_CONF_PEX BIT(8) /* Protocol Exception Handling */ +#define REG_CONF_FDRF BIT(10) /* Ignore Remote Frame Enable */ + +/* Bitfields of Status register */ +#define REG_STAT_RXNE BIT(0) /* Receive Buffer Not Empty Status */ +#define REG_STAT_DOR BIT(1) /* Receive Data Overflow Status */ +#define REG_STAT_EFT BIT(3) /* Error Frame Flag */ +#define REG_STAT_RXS BIT(4) /* Receive Status Flag */ +#define REG_STAT_TXS BIT(5) /* Transmission Status Flag */ +#define REG_STAT_EWL BIT(6) /* Transmission Error Threshold Flag */ +#define REG_STAT_IDLE BIT(7) /* Bus Idle Flag */ +#define REG_STAT_PEXS BIT(8) /* Protocol Exception Status */ +#define REG_STAT_STCNT BIT(16) /* Transmission Count Support */ + +/* Bitfields of Command register */ +#define REG_CMD_RXRPMV BIT(1) /* Receive Buffer Read-to-Move */ +#define REG_CMD_RRB BIT(2) /* Receive Buffer Release */ +#define REG_CMD_CDO BIT(3) /* Received Data Overflow Status Clear */ +#define REG_CMD_ERCRST BIT(4) /* Error Count Clear */ +#define REG_CMD_RXFCRST BIT(5) /* Receive Frame Count Clear */ +#define REG_CMD_TXFCRST BIT(6) /* Transmit Frame Count Clear */ +#define REG_CMD_CPEXS BIT(7) /* Exception Status Clear */ + +/* Bitfields of Interrupt Status register */ +#define REG_INT_STAT_RXI BIT(0) +#define REG_INT_STAT_TXI BIT(1) +#define REG_INT_STAT_EWLI BIT(2) +#define REG_INT_STAT_DOI BIT(3) +#define REG_INT_STAT_FCSI BIT(4) +#define REG_INT_STAT_ALI BIT(5) +#define REG_INT_STAT_BEI BIT(6) +#define REG_INT_STAT_RXFI BIT(7) +#define REG_INT_STAT_BSI BIT(8) +#define REG_INT_STAT_RBNEI BIT(9) +#define REG_INT_STAT_TXBHCI BIT(10) +#define REG_INT_STAT_OFI BIT(11) +#define REG_INT_STAT_DMADI BIT(12) + +#define REG_INT_STAT_ERRORI (REG_INT_STAT_EWLI | REG_INT_STAT_FCSI |\ + REG_INT_STAT_ALI | REG_INT_STAT_BEI) + +/* Bitfields of Interrupt Enable register */ +#define REG_INT_ENA_CLR GENMASK(28, 16) +#define REG_INT_ENA_SET GENMASK(12, 0) + +/* Bitfields of Interrupt Mask register */ +#define REG_INT_MASK_SET GENMASK(12, 0) +#define REG_INT_MASK_CLR GENMASK(28, 16) + +/* Bitfields of Normal Rate Configuration register */ +#define REG_BTR_PROP GENMASK(6, 0) +#define REG_BTR_PH1 GENMASK(12, 7) +#define REG_BTR_PH2 GENMASK(18, 13) +#define REG_BTR_BRP GENMASK(22, 19) +#define REG_BTR_SJW GENMASK(31, 27) + +/* Bitfields of FD Data Rate Configuration register */ +#define REG_BTR_FD_PROP GENMASK(6, 0) +#define REG_BTR_FD_PH1 GENMASK(11, 7) +#define REG_BTR_FD_PH2 GENMASK(17, 13) +#define REG_BTR_FD_BRP GENMASK(26, 19) +#define REG_BTR_FD_SJW GENMASK(31, 27) + +/* Bitfields of Error Threshold Configuration register */ +#define REG_ERL_ERP GENMASK(7, 0) +#define REG_ERL_EW GENMASK(23, 16) + +/* Bitfields of Error Status register */ +#define REG_FSTAT_ERA BIT(0) +#define REG_FSTAT_ERP BIT(1) +#define REG_FSTAT_BOF BIT(2) + +#define REG_FSTAT_MASK GENMASK(2, 0) + +/* Bitfields of Error Count register */ +#define REG_ERC_TEC GENMASK(8, 0) +#define REG_ERC_REC GENMASK(24, 16) + +/* Bitfields of Rate Error Count register */ +#define REG_BRE_NORM GENMASK(15, 0) +#define REG_BRE_FD_DATA GENMASK(31, 16) + +/* Bitfields of Error Count Debug register */ +#define REG_CTR_PRES_CTPV GENMASK(8, 0) +#define REG_CTR_PRES_PTX BIT(9) +#define REG_CTR_PRES_PRX BIT(10) + +/* Bitfields of Error Capture Status register */ +#define REG_ERR_CAPT_POS GENMASK(4, 0) +#define REG_ERR_CAPT_TYPE GENMASK(7, 5) + +/* Bitfields of Retransmission Count register */ +#define REG_RETX_CNT_VAL GENMASK(3, 0) + +/* Bitfields of Lost Arbitration Capture register */ +#define REG_ALC_BIT_POS GENMASK(4, 0) +#define REG_ALC_ID_FIELD GENMASK(7, 5) + +/* Bitfields of Transmission Delay Measurement register */ +#define REG_TRV_DLY_VAL GENMASK(6, 0) + +/* Bitfields of Second Sampling Point Configuration register */ +#define REG_SSP_CFG_OFF GENMASK(7, 0) +#define REG_SSP_CFG_SRC GENMASK(9, 8) +#define REG_SSP_CFG_SAT BIT(10) + +/* Bitfields of Receive Message Count register */ +#define REG_RX_FR_CNT_VAL GENMASK(31, 0) + +/* Bitfields of Transmit Message Count register */ +#define REG_TX_FR_CNT_VAL GENMASK(31, 0) + +/* Bitfields of Debug register */ +#define REG_DEBUG_STF_CNT GENMASK(2, 0) +#define REG_DEBUG_DSTF_CNT GENMASK(5, 3) +#define REG_DEBUG_PC_ARB BIT(6) +#define REG_DEBUG_PC_CON BIT(7) +#define REG_DEBUG_PC_DAT BIT(8) +#define REG_DEBUG_PC_STC BIT(9) +#define REG_DEBUG_PC_CRC BIT(10) +#define REG_DEBUG_PC_CRCD BIT(11) +#define REG_DEBUG_PC_ACK BIT(12) +#define REG_DEBUG_PC_ACKD BIT(13) +#define REG_DEBUG_PC_EOF BIT(14) +#define REG_DEBUG_PC_INT BIT(15) +#define REG_DEBUG_PC_SUSP BIT(16) +#define REG_DEBUG_PC_OVR BIT(17) +#define REG_DEBUG_PC_SOF BIT(18) + +/* Bitfields of Timestamp register */ +#define REG_TS_TIMESTAMP GENMASK(15, 0) +#define REG_TS_PSC GENMASK(24, 16) + +/* Bitfields of Fractional Divider Ratio register */ +#define REG_FRC_FRC_DBT GENMASK(15, 8) +#define REG_FRC_FRC_NBT GENMASK(7, 0) + +/* Bitfields of Filter A Mask register */ +#define REG_FIL_A_MASK GENMASK(28, 0) + +/* Bitfields of Filter A value register */ +#define REG_FIL_A_VAL GENMASK(28, 0) + +/* Bitfields of Filter B Mask register */ +#define REG_FIL_B_MASK GENMASK(28, 0) + +/* Bitfields of Filter B value register */ +#define REG_FIL_B_VAL GENMASK(28, 0) + +/* Bitfields of Filter C Mask register */ +#define REG_FIL_C_MASK GENMASK(28, 0) + +/* Bitfields of Filter C value register */ +#define REG_FIL_C_VAL GENMASK(28, 0) + +/* Bitfields of Range Filter Low Threshold register */ +#define REG_FIL_R_LOW_VAL GENMASK(28, 0) + +/* Bitfields of Range Filter High Threshold register */ +#define REG_FIL_R_HI_VAL GENMASK(28, 0) + +/* Bitfields of Filter Control register */ +#define REG_FIL_CTRL_FANB BIT(0) +#define REG_FIL_CTRL_FANE BIT(1) +#define REG_FIL_CTRL_FAFB BIT(2) +#define REG_FIL_CTRL_FAFE BIT(3) +#define REG_FIL_CTRL_FBNB BIT(4) +#define REG_FIL_CTRL_FBNE BIT(5) +#define REG_FIL_CTRL_FBFB BIT(6) +#define REG_FIL_CTRL_FBFE BIT(7) +#define REG_FIL_CTRL_FCNB BIT(8) +#define REG_FIL_CTRL_FCNE BIT(9) +#define REG_FIL_CTRL_FCFB BIT(10) +#define REG_FIL_CTRL_FCFE BIT(11) +#define REG_FIL_CTRL_FRNB BIT(12) +#define REG_FIL_CTRL_FRNE BIT(13) +#define REG_FIL_CTRL_FRFB BIT(14) +#define REG_FIL_CTRL_FRFE BIT(15) +#define REG_FIL_CTRL_SFA BIT(16) +#define REG_FIL_CTRL_SFB BIT(17) +#define REG_FIL_CTRL_SFC BIT(18) +#define REG_FIL_CTRL_SFR BIT(19) + +/* Bitfields of Receive Buffer Information register */ +#define REG_RX_MEM_INFO_BUFF_SIZE GENMASK(12, 0) +#define REG_RX_MEM_INFO_MEM_FREE GENMASK(28, 16) + +/* Bitfields of Receive Buffer Pointer register */ +#define REG_RX_PTR_WPP GENMASK(11, 0) +#define REG_RX_PTR_RPP GENMASK(27, 16) + +/* Bitfields of Receive Buffer Status register */ +#define REG_RX_STAT_RXE BIT(0) +#define REG_RX_STAT_RXF BIT(1) +#define REG_RX_STAT_RXMOF BIT(2) +#define REG_RX_STAT_RXFRC GENMASK(14, 4) +#define REG_RX_STAT_RTSOP BIT(16) + +/* Bitfields of Receive Data register */ +#define REG_RX_DATA_VAL GENMASK(31, 0) + +/* Bitfields of Transmit Buffer Status register */ +#define REG_TX_STAT_BRP GENMASK(7, 0) +#define REG_TX_STAT_TXS GENMASK(10, 8) +#define REG_TX_STAT_BS GENMASK(31, 16) + +#define REG_TX_STAT_BS_TX_MASK_BASE GENMASK(17, 16) + +/* Bitfields of Transmit Command register */ +#define REG_TX_CMD_BAR GENMASK(7, 0) +#define REG_TX_CMD_BCR GENMASK(15, 8) +#define REG_TX_CMD_BSC GENMASK(23, 16) + +/* Bitfields of Transmit Buffer Selection register */ +#define REG_TX_SEL_BUF_SEL GENMASK(3, 0) +#define REG_TX_SEL_BUF_CNT GENMASK(7, 4) + +/* Loongson CANFD Frame format */ +#define LOONGSON_CANFD_FRAME_META0 0x0 +#define LOONGSON_CANFD_FRAME_META1 0x4 +#define LOONGSON_CANFD_FRAME_DB_1 0x8 + +/* Bitfields of FRAME META0 */ +#define REG_FRAME_META0_ID_EXT GENMASK(17, 0) +#define REG_FRAME_META0_ID_BASE GENMASK(28, 18) +#define REG_FRAME_META0_RTR BIT(29) /* Remote Transmission Request, only in CAN2.0 */ +/* Extended Identifier Type. 1: 29 bits ID, 0: 11 bits ID. */ +#define REG_FRAME_META0_XDT BIT(30) +#define REG_FRAME_META0_ESI BIT(31) /* Error State Indicator, only in CAN-FD */ + +/* Bitfields of FRAME META1 */ +#define REG_FRAME_META1_TIMESTAMP GENMASK(15, 0) +#define REG_FRAME_META1_DLC GENMASK(19, 16) /* Data Length Code */ +#define REG_FRAME_META1_BRS BIT(20) /* Bit Rate Shift, Only in CANFD */ +/* Flexible Data-rate Format. 1: CANFD, 0: CAN2.0 */ +#define REG_FRAME_META1_FDF BIT(21) +#define REG_FRAME_META1_RWCNT GENMASK(28, 24) /* Read Word Counter */ + +/* Bitfields of FRAME TEST */ +#define REG_FRAME_TEST_FSTC BIT(0) +#define REG_FRAME_TEST_FCRC BIT(1) +#define REG_FRAME_TEST_SDLC BIT(2) +#define REG_FRAME_TEST_TPRM GENMASK(12, 8) + +#define DEV_NAME "loongson_canfd" +#define LOONGSON_CANFD_ID 0xBABE +#define LOONGSON_CANFD_DW_BYTE 4 +#define LOONGSON_CANFD_TXBUF_NUM 8 +#define LOONGSON_CANFD_MAX_RTXTH 0xf + +/** + * struct loongson_canfd_priv - This definition define CAN driver instance + * @can: CAN private data structure. + * @napi: NAPI structure + * @regmap: regmap of the CAN device + * @res: Pointer to the CAN device respurce + * @tx_lock: Lock for synchronizing TX interrupt handling + */ +struct loongson_canfd_priv { + struct can_priv can; /* must be first member! */ + struct napi_struct napi; + struct regmap *regmap; + struct resource *res; + spinlock_t tx_lock; /* protect the sending queue */ +}; + +/** + * enum loongson_canfd_txbuf_sts - status of TX buffer + * @TX_BS_IDLE: Status processed or not sent. + * @TX_BS_VALID: Sending successful. + * @TX_BS_FAIL: Sending failed. + * @TX_BS_CANCEL: Sending cancelled. + */ +enum loongson_canfd_txbuf_sts { + TX_BS_IDLE = 0x0, + TX_BS_VALID = 0x1, + TX_BS_FAIL = 0x2, + TX_BS_CANCEL = 0x3 +}; + +/** + * enum loongson_canfd_txbuf_cmd - command of TX buffer + * @TXT_CMD_ADD: Buffer add request. + * @TXT_CMD_CANCEL: Buffer cancel request. + * @TXT_CMD_SR_CLEAN: Buffer send record clear. + * 1: to clear the send completion record (LOONGSON_CANFD_TX_STAT[BS]). + */ +enum loongson_canfd_txbuf_cmd { + TXT_CMD_ADD = 0x01, + TXT_CMD_CANCEL = 0x02, + TXT_CMD_SR_CLEAN = 0x03 +}; + +static const struct can_bittiming_const loongson_canfd_bit_timing = { + .name = DEV_NAME, + .tseg1_min = 2, + .tseg1_max = 190, + .tseg2_min = 2, + .tseg2_max = 63, + .sjw_max = 31, + .brp_min = 1, + .brp_max = 15, + .brp_inc = 1, +}; + +static const struct can_bittiming_const loongson_canfd_bit_timing_data = { + .name = DEV_NAME, + .tseg1_min = 2, + .tseg1_max = 190, + .tseg2_min = 2, + .tseg2_max = 63, + .sjw_max = 31, + .brp_min = 1, + .brp_max = 255, + .brp_inc = 1, +}; + +/* CAN FD Transmission Delay Compensation constants */ +static const struct can_tdc_const loongson_canfd_tdc_data = { + .tdcv_min = 0, + .tdcv_max = 0, + .tdco_min = 1, + .tdco_max = 127, + .tdcf_min = 0, /* Filter window not supported */ + .tdcf_max = 0, +}; + +/** + * loongson_canfd_enabled() - Gets the controller is enabled + * @priv: Pointer to private data + * + * Return: True - the controller is enabled. + * False - the controller is disabled. + */ +static bool loongson_canfd_enabled(struct loongson_canfd_priv *priv) +{ + return !!regmap_test_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA); +} + +/** + * loongson_canfd_txbuf_freed() - Gets the flag for TX buffer to be sent + * @priv: Pointer to private data + * + * Return: True - TX buffer is empty. + * False - TX buffer is processing + */ +static bool loongson_canfd_txbuf_freed(struct loongson_canfd_priv *priv) +{ + return !regmap_test_bits(priv->regmap, LOONGSON_CANFD_TX_STAT, REG_TX_STAT_BRP); +} + +/** + * loongson_canfd_get_txbuf_sts() - Gets status of TX buffer + * @priv: Pointer to private data + * @buf_id: Buffer index (0-based) + * + * Return: Status of TX buffer + */ +static enum loongson_canfd_txbuf_sts +loongson_canfd_get_txbuf_sts(struct loongson_canfd_priv *priv, u8 buf_id) +{ + u32 sts, mask; + + mask = REG_TX_STAT_BS_TX_MASK_BASE << 2 * buf_id; + regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &sts); + + return field_get(mask, sts); +} + +/** + * loongson_canfd_get_txbuf_id() - Gets the index of a non-idle TX buffer + * @priv: Pointer to private data + * @sts: Status of TXT buffer to outside + * + * Return: Buffer index (0-based) + */ +static unsigned int loongson_canfd_get_txbuf_id(struct loongson_canfd_priv *priv, + enum loongson_canfd_txbuf_sts *sts) +{ + unsigned int i; + + for (i = 0; i < LOONGSON_CANFD_TXBUF_NUM; i++) { + if (loongson_canfd_get_txbuf_sts(priv, i)) { + *sts = loongson_canfd_get_txbuf_sts(priv, i); + break; + } + } + + if (i == LOONGSON_CANFD_TXBUF_NUM) + *sts = TX_BS_IDLE; + + return i; +} + +/** + * loongson_canfd_txbuf_is_writable() - Checks if frame can be inserted to TX Buffer + * @priv: Pointer to private data + * @buf_id: Buffer index (0-based) + * + * Return: True - Frame can be inserted to TXT Buffer, + * False - If attempted, frame will not be inserted to TX Buffer + */ +static bool loongson_canfd_txbuf_is_writable(struct loongson_canfd_priv *priv, u8 buf_id) +{ + enum loongson_canfd_txbuf_sts bs; + + bs = loongson_canfd_get_txbuf_sts(priv, buf_id); + if (bs) + return false; + + return !regmap_test_bits(priv->regmap, LOONGSON_CANFD_TX_STAT, BIT(buf_id)); +} + +/** + * loongson_canfd_set_txbuf_cmd() - Applies command on TX buffer + * @ndev: Pointer to net_device structure + * @cmd: Command to set + * @buf_id: Buffer index (0-based) + */ +static void loongson_canfd_set_txbuf_cmd(struct net_device *ndev, + enum loongson_canfd_txbuf_cmd cmd, u8 buf_id) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + u32 mask; + + switch (cmd) { + case TXT_CMD_ADD: + mask = REG_TX_CMD_BAR; + break; + case TXT_CMD_CANCEL: + mask = REG_TX_CMD_BCR; + break; + case TXT_CMD_SR_CLEAN: + mask = REG_TX_CMD_BSC; + break; + default: + netdev_err(ndev, "Unknown command id: %x\n", cmd); + return; + } + + regmap_write(priv->regmap, LOONGSON_CANFD_TX_CMD, field_prep(mask, BIT(buf_id))); +} + +/** + * loongson_canfd_rxbuf_empty() - Gets the RX buffer is empty + * @priv: Pointer to private data + * + * Return: True - RX buffer is empty. + * False - RX buffer is processing + */ +static bool loongson_canfd_rxbuf_empty(struct loongson_canfd_priv *priv) +{ + return !!regmap_test_bits(priv->regmap, LOONGSON_CANFD_RX_STAT, REG_RX_STAT_RXE); +} + +/** + * loongson_canfd_reset() - Issues software reset request to Loongson CANFD + * @ndev: Pointer to net_device structure + */ +static void loongson_canfd_reset(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + + regmap_write(priv->regmap, LOONGSON_CANFD_MODE, REG_MODE_RST); + regmap_write(priv->regmap, LOONGSON_CANFD_MODE, REG_MODE_RXBAM | REG_MODE_BUFM); +} + +/** + * loongson_canfd_set_btr() - Sets CAN bus bit timing in Loongson CANFD + * @ndev: Pointer to net_device structure + * @bt: Pointer to Bit timing structure + * @nominal: True - Nominal bit timing, False - Data bit timing + */ +static void loongson_canfd_set_btr(struct net_device *ndev, struct can_bittiming *bt, bool nominal) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + u32 ph1_max = FIELD_MAX(REG_BTR_PH1); + u32 btr = 0; + + /* + * The timing calculation functions have only constraints on tseg1, + * which is prop_seg + phase1_seg combined. + * tseg1 is then split in half and stored into prog_seg and phase_seg1. + * In Loongson CAN-FD, PROP is 7 bits wide but PH1 only 6, so we must + * re-distribute the values here. + */ + if (bt->phase_seg1 > ph1_max) { + bt->prop_seg += bt->phase_seg1 - ph1_max; + bt->phase_seg1 = ph1_max; + } + + if (nominal) { + btr = FIELD_PREP(REG_BTR_PROP, bt->prop_seg) | + FIELD_PREP(REG_BTR_PH1, bt->phase_seg1) | + FIELD_PREP(REG_BTR_PH2, bt->phase_seg2) | + FIELD_PREP(REG_BTR_BRP, bt->brp) | + FIELD_PREP(REG_BTR_SJW, bt->sjw); + + regmap_write(priv->regmap, LOONGSON_CANFD_BTR_NORM, btr); + } else { + btr = FIELD_PREP(REG_BTR_FD_PROP, bt->prop_seg) | + FIELD_PREP(REG_BTR_FD_PH1, bt->phase_seg1) | + FIELD_PREP(REG_BTR_FD_PH2, bt->phase_seg2) | + FIELD_PREP(REG_BTR_FD_BRP, bt->brp) | + FIELD_PREP(REG_BTR_FD_SJW, bt->sjw); + + regmap_write(priv->regmap, LOONGSON_CANFD_BTR_FD, btr); + } +} + +/** + * loongson_canfd_set_bittiming() - CAN set nominal bit timing routine + * @ndev: Pointer to net_device structure + * + * Return: 0 on success, -%EPERM on error + */ +static int loongson_canfd_set_bittiming(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + + /* Note that bt may be modified here */ + loongson_canfd_set_btr(ndev, bt, true); + + return 0; +} + +/** + * loongson_canfd_set_data_bittiming() - CAN set data bit timing routine + * @ndev: Pointer to net_device structure + * + * Return: 0 on success, -%EPERM on error + */ +static int loongson_canfd_set_data_bittiming(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + struct can_bittiming *dbt = &priv->can.fd.data_bittiming; + + /* Note that dbt may be modified here */ + loongson_canfd_set_btr(ndev, dbt, false); + + return 0; +} + +/** + * loongson_canfd_get_auto_tdcv - Get Transmitter Delay Compensation Value + * @ndev: Pointer to net_device structure + * @tdcv: Pointer to TDCV value + * + * Return: 0 on success + */ +static int loongson_canfd_get_auto_tdcv(const struct net_device *ndev, u32 *tdcv) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + u32 val; + + regmap_read(priv->regmap, LOONGSON_CANFD_TRV_DLY, &val); + *tdcv = FIELD_GET(REG_TRV_DLY_VAL, val); + + return 0; +} + +/** + * loongson_canfd_set_secondary_sample_point() - Set secondary sample point in Loongson CANFD + * @ndev: Pointer to net_device structure + * + * Return: 0 on success, -%EPERM if controller is enabled + */ +static int loongson_canfd_set_secondary_sample_point(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + u32 ssp_cfg; + + if (loongson_canfd_enabled(priv)) { + netdev_err(ndev, "BUG! Cannot set SSP - CAN is enabled\n"); + return -EPERM; + } + + if (can_fd_tdc_is_enabled(&priv->can)) + ssp_cfg = FIELD_PREP(REG_SSP_CFG_OFF, priv->can.fd.tdc.tdco) | + FIELD_PREP(REG_SSP_CFG_SRC, 0x0); + else + ssp_cfg = FIELD_PREP(REG_SSP_CFG_SRC, 0x1); + + regmap_write(priv->regmap, LOONGSON_CANFD_SSP_CFG, ssp_cfg); + + return 0; +} + +/** + * loongson_canfd_set_conf_mode() - Sets Loongson CANFD mode and configure registers + * @priv: Pointer to private data + */ +static void loongson_canfd_set_conf_mode(struct loongson_canfd_priv *priv) +{ + u32 ctrlmode = priv->can.ctrlmode; + u32 mode, conf; + + regmap_read(priv->regmap, LOONGSON_CANFD_MODE, &mode); + + if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + mode |= REG_MODE_BMM; + else + mode &= ~REG_MODE_BMM; + + if (ctrlmode & CAN_CTRLMODE_FD) + mode |= REG_MODE_FDE; + else + mode &= ~REG_MODE_FDE; + + if (ctrlmode & CAN_CTRLMODE_PRESUME_ACK) + mode |= REG_MODE_ACF; + else + mode &= ~REG_MODE_ACF; + + /* + * Some bits fixed: + * TSTM - Off, User shall not be able to change REC/TEC by hand + * during operation + */ + mode &= ~REG_MODE_TSTM; + regmap_write(priv->regmap, LOONGSON_CANFD_MODE, mode); + + regmap_read(priv->regmap, LOONGSON_CANFD_CONF, &conf); + + if (ctrlmode & CAN_CTRLMODE_LOOPBACK) + conf |= REG_CONF_ILBP; + else + conf &= ~REG_CONF_ILBP; + + if (ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + conf |= REG_CONF_NISOFD; + else + conf &= ~REG_CONF_NISOFD; + + /* One shot mode supported indirectly via Retransmit limit */ + conf &= ~FIELD_PREP(REG_CONF_RTRTH, LOONGSON_CANFD_MAX_RTXTH); + + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) + conf |= REG_CONF_RTRLE; + else + conf |= REG_CONF_RTRLE | FIELD_PREP(REG_CONF_RTRTH, LOONGSON_CANFD_MAX_RTXTH); + + regmap_write(priv->regmap, LOONGSON_CANFD_CONF, conf); +} + +/** + * loongson_canfd_chip_start() - This routine starts the driver + * @ndev: Pointer to net_device structure + * + * Return: 0 on success and failure value on error + */ +static int loongson_canfd_chip_start(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + u16 int_ena; + int ret; + + /* Configure bit-rates and ssp */ + ret = loongson_canfd_set_bittiming(ndev); + if (ret < 0) + return ret; + + ret = loongson_canfd_set_data_bittiming(ndev); + if (ret < 0) + return ret; + + ret = loongson_canfd_set_secondary_sample_point(ndev); + if (ret < 0) + return ret; + + /* Setting mode and configure registers */ + loongson_canfd_set_conf_mode(priv); + + /* Configure interrupts */ + int_ena = REG_INT_STAT_RBNEI | REG_INT_STAT_TXBHCI | + REG_INT_STAT_EWLI | REG_INT_STAT_FCSI; + + /* Bus error reporting */ + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + int_ena |= REG_INT_STAT_ALI | REG_INT_STAT_BEI; + + /* It's after reset, so there is no need to clear anything */ + regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, ~int_ena); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_ENA, int_ena); + + /* Controller enters ERROR_ACTIVE on initial FCSI */ + priv->can.state = CAN_STATE_STOPPED; + + /* Enable the controller */ + regmap_update_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA, REG_CONF_ENA); + + return 0; +} + +/** + * loongson_canfd_do_set_mode() - Sets mode of the driver + * @ndev: Pointer to net_device structure + * @mode: Tells the mode of the driver + * + * This check the drivers state and calls the corresponding modes to set. + * + * Return: 0 on success and failure value on error + */ +static int loongson_canfd_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + loongson_canfd_reset(ndev); + + ret = loongson_canfd_chip_start(ndev); + if (ret < 0) { + netdev_err(ndev, "loongson_canfd_chip_start failed!\n"); + return ret; + } + + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +/** + * loongson_canfd_insert_frame() - Inserts frame to TXT buffer + * @priv: Pointer to private data + * @cf: Pointer to CAN frame to be inserted + * @buf: TXT Buffer index to which frame is inserted (0-based) + * @isfdf: True - CAN FD Frame, False - CAN 2.0 Frame + * + * Return: + * * True - Frame inserted successfully + * * False - Frame was not inserted due to one of: + * 1. TXT Buffer is not writable (it is in wrong state) + * 2. Invalid TXT buffer index + * 3. Invalid frame length + */ +static bool loongson_canfd_insert_frame(struct loongson_canfd_priv *priv, + const struct canfd_frame *cf, u8 buf, bool isfdf) +{ + u32 meta0, meta1 = 0; + + if (buf >= LOONGSON_CANFD_TXBUF_NUM) + return false; + + if (!loongson_canfd_txbuf_is_writable(priv, buf)) + return false; + + if (cf->len > CANFD_MAX_DLEN) + return false; + + /* Prepare identifier */ + if (cf->can_id & CAN_EFF_FLAG) { + meta0 = cf->can_id & CAN_EFF_MASK; + meta0 |= REG_FRAME_META0_XDT; + } else { + meta0 = FIELD_PREP(REG_FRAME_META0_ID_BASE, cf->can_id & CAN_SFF_MASK); + } + + /* Prepare Frame format */ + if (cf->can_id & CAN_RTR_FLAG) + meta0 |= REG_FRAME_META0_RTR; + + if (isfdf) { + meta1 = REG_FRAME_META1_FDF; + + if (cf->flags & CANFD_BRS) + meta1 |= REG_FRAME_META1_BRS; + } + + meta1 |= FIELD_PREP(REG_FRAME_META1_DLC, can_fd_len2dlc(cf->len)); + + /* TXT buffer select */ + regmap_write(priv->regmap, LOONGSON_CANFD_TX_SEL, buf); + + /* Write ID, Frame format */ + regmap_write(priv->regmap, LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_META0, meta0); + regmap_write(priv->regmap, LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_META1, meta1); + + if (cf->can_id & CAN_RTR_FLAG) + return true; + + /* Write Data payload */ + for (unsigned int i = 0; i < cf->len; i += LOONGSON_CANFD_DW_BYTE) { + regmap_write(priv->regmap, + LOONGSON_CANFD_TX_DATA_1 + LOONGSON_CANFD_FRAME_DB_1 + i, + *(u32 *)(cf->data + i)); + } + + return true; +} + +/** + * loongson_canfd_start_xmit() - Starts the transmission + * @skb: sk_buff pointer that contains data to be Txed + * @ndev: Pointer to net_device structure + * + * Invoked from upper layers to initiate transmission. Uses the next available free TX Buffer and + * populates its fields to start the transmission. + * + * Return: %NETDEV_TX_OK on success, + * %NETDEV_TX_BUSY when no free TX buffer is available, negative return values reserved + * for error cases. + */ +static netdev_tx_t loongson_canfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + struct loongson_canfd_priv *priv = netdev_priv(ndev); + u32 buf_id = 0, tx_stat, i = 0; + unsigned long tx_brp; + u16 tx_bs; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (unlikely(!loongson_canfd_txbuf_freed(priv))) { + netif_stop_queue(ndev); + netdev_err(ndev, "BUG!, no TXB free when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + guard(spinlock_irqsave)(&priv->tx_lock); + + regmap_read(priv->regmap, LOONGSON_CANFD_TX_STAT, &tx_stat); + tx_brp = FIELD_GET(REG_TX_STAT_BRP, tx_stat); + tx_bs = FIELD_GET(REG_TX_STAT_BS, tx_stat); + + for_each_clear_bit(i, &tx_brp, LOONGSON_CANFD_TXBUF_NUM) { + u32 transmission_complete_mask = 0x3 << (i * 2); + + if (!(tx_bs & transmission_complete_mask)) { + buf_id = i; + break; + } + } + + if (!loongson_canfd_insert_frame(priv, cf, buf_id, can_is_canfd_skb(skb))) { + netdev_err(ndev, "TXNF set but cannot insert frame into TXTB! HW Bug?"); + kfree_skb(skb); + ndev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + can_put_echo_skb(skb, ndev, buf_id, 0); + + loongson_canfd_set_txbuf_cmd(ndev, TXT_CMD_ADD, buf_id); + + /* Check if all TX buffers are full */ + if (!loongson_canfd_txbuf_freed(priv)) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +/** + * loongson_canfd_read_rx_frame() - Reads frame from RX FIFO + * @priv: Pointer to private data + * @cf: Pointer to CAN frame struct + * @meta0: The first `frame format` read previously + * @meta1: The second `frame format` read previously + */ +static void loongson_canfd_read_rx_frame(struct loongson_canfd_priv *priv, struct canfd_frame *cf, + u32 meta0, u32 meta1) +{ + u32 data, i, wc, len; + + /* Extended Identifier Type */ + if (meta0 & REG_FRAME_META0_XDT) + cf->can_id = (meta0 & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = FIELD_GET(REG_FRAME_META0_ID_BASE, meta0) & CAN_SFF_MASK; + + /* BRS, ESI, RTR Flags */ + cf->flags = 0; + + if (meta1 & REG_FRAME_META1_FDF) { + if (meta1 & REG_FRAME_META1_BRS) + cf->flags |= CANFD_BRS; + + if (meta0 & REG_FRAME_META0_ESI) + cf->flags |= CANFD_ESI; + } else if (meta0 & REG_FRAME_META0_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } + + wc = FIELD_GET(REG_FRAME_META1_RWCNT, meta1) - 2; + + /* Data Length Code */ + len = FIELD_GET(REG_FRAME_META1_DLC, meta1); + if (len > 8) { + if (meta1 & REG_FRAME_META1_FDF) + len = wc << 2; + else + len = 8; + } + + cf->len = len; + if (unlikely(len > wc * LOONGSON_CANFD_DW_BYTE)) + len = wc * LOONGSON_CANFD_DW_BYTE; + + /* Data */ + for (i = 0; i < len; i += LOONGSON_CANFD_DW_BYTE) + regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, (u32 *)(cf->data + i)); + + while (unlikely(i < wc * LOONGSON_CANFD_DW_BYTE)) { + regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data); + i += LOONGSON_CANFD_DW_BYTE; + } +} + +/** + * loongson_canfd_rx() - Called from CAN ISR to complete the received frame processing + * @ndev: Pointer to net_device structure + * + * This function is invoked from the CAN isr to process the Rx frames. It does minimal + * processing and invokes "netif_receive_skb" to complete further processing. + * Return: 1 when frame is passed to the network layer, 0 when the first frame word is read but + * system is out of free SKBs temporally and left code to resolve SKB allocation later, + * -%EAGAIN in a case of empty Rx FIFO. + */ +static int loongson_canfd_rx(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + u32 meta0, meta1; + + regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta0); + regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta1); + + /* Number of characters received */ + if (!FIELD_GET(REG_FRAME_META1_RWCNT, meta1)) + return -EAGAIN; + + /* Flexible Data-rate Format */ + if (meta1 & REG_FRAME_META1_FDF) + skb = alloc_canfd_skb(ndev, &cf); + else + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); + if (unlikely(!skb)) + return 0; + + loongson_canfd_read_rx_frame(priv, cf, meta0, meta1); + + if (cf->can_id != CAN_RTR_FLAG) + stats->rx_bytes += cf->len; + stats->rx_packets++; + netif_receive_skb(skb); + + return 1; +} + +/** + * loongson_canfd_read_fault_state() - Reads Loongson CANFD fault state. + * @ndev: Pointer to net_device structure + * + * Returns: Fault confinement state of controller + */ +static enum can_state loongson_canfd_read_fault_state(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + enum can_state sts = CAN_STATE_ERROR_PASSIVE; + u32 fstat; + + regmap_read(priv->regmap, LOONGSON_CANFD_FSTAT, &fstat); + + switch (FIELD_GET(REG_FSTAT_MASK, fstat)) { + case REG_FSTAT_ERA: + u32 ewl, erl, rec_tec, max_tec; + + regmap_read(priv->regmap, LOONGSON_CANFD_ERL, &erl); + regmap_read(priv->regmap, LOONGSON_CANFD_ERC, &rec_tec); + + ewl = FIELD_GET(REG_ERL_EW, erl); + max_tec = max(FIELD_GET(REG_ERC_REC, rec_tec), FIELD_GET(REG_ERC_TEC, rec_tec)); + + if (ewl > max_tec) + sts = CAN_STATE_ERROR_ACTIVE; + else + sts = CAN_STATE_ERROR_WARNING; + break; + case REG_FSTAT_ERP: + sts = CAN_STATE_ERROR_PASSIVE; + break; + case REG_FSTAT_BOF: + sts = CAN_STATE_BUS_OFF; + break; + default: + netdev_err(ndev, "Invalid error state.\n"); + break; + } + + return sts; +} + +/** + * loongson_canfd_get_bec() - Reads REC/TEC counter values from controller + * @priv: Pointer to private data + * @bec: Pointer to Error counter structure + */ +static void loongson_canfd_get_bec(struct loongson_canfd_priv *priv, struct can_berr_counter *bec) +{ + u32 erc; + + regmap_read(priv->regmap, LOONGSON_CANFD_ERC, &erc); + bec->rxerr = FIELD_GET(REG_ERC_REC, erc); + bec->txerr = FIELD_GET(REG_ERC_TEC, erc); +} + +/** + * loongson_canfd_get_berr_counter() - error counter routine + * @ndev: Pointer to net_device structure + * @bec: Pointer to can_berr_counter structure + * + * Return: 0 always + */ +static int loongson_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + + loongson_canfd_get_bec(priv, bec); + return 0; +} + +/** + * loongson_canfd_err_interrupt() - Error frame ISR + * @ndev: net_device pointer + * @isr: interrupt status register value + * + * This is the CAN error interrupt and it will check the type of error and forward the error + * frame to upper layers. + */ +static void loongson_canfd_err_interrupt(struct net_device *ndev, u32 isr) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_berr_counter bec; + enum can_state state; + struct can_frame *cf; + struct sk_buff *skb; + u32 err_capt, alc; + + loongson_canfd_get_bec(priv, &bec); + + state = loongson_canfd_read_fault_state(ndev); + regmap_read(priv->regmap, LOONGSON_CANFD_ERR_CAPT, &err_capt); + regmap_read(priv->regmap, LOONGSON_CANFD_ALC, &alc); + + netdev_dbg(ndev, "%s: ISR 0x%08x, rxerr %d, txerr %d, error type %lu, pos %lu, ALC id_field %lu, bit %lu\n", + __func__, isr, bec.rxerr, bec.txerr, + FIELD_GET(REG_ERR_CAPT_TYPE, err_capt), + FIELD_GET(REG_ERR_CAPT_POS, err_capt), + FIELD_GET(REG_ALC_ID_FIELD, alc), + FIELD_GET(REG_ALC_BIT_POS, alc)); + + skb = alloc_can_err_skb(ndev, &cf); + + /* + * EWLI: error warning limit condition met + * FCSI: fault confinement state changed + * ALI: arbitration lost (just informative) + * BEI: bus error interrupt + */ + if ((isr & REG_INT_STAT_FCSI) || (isr & REG_INT_STAT_EWLI)) { + netdev_info(ndev, "state changes from %s to %s\n", + can_get_state_str(priv->can.state), can_get_state_str(state)); + + if (priv->can.state == state) + netdev_warn(ndev, "cur and pre state is the same!(miss intr?)\n"); + + isr = REG_INT_STAT_FCSI | REG_INT_STAT_EWLI; + priv->can.state = state; + switch (state) { + case CAN_STATE_BUS_OFF: + priv->can.can_stats.bus_off++; + if (priv->can.restart_ms) + regmap_write(priv->regmap, LOONGSON_CANFD_CMD, + REG_CMD_ERCRST); + + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + break; + case CAN_STATE_ERROR_PASSIVE: + priv->can.can_stats.error_passive++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; + if (bec.rxerr >= CAN_ERROR_PASSIVE_THRESHOLD) + cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE; + else + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + } + break; + case CAN_STATE_ERROR_WARNING: + priv->can.can_stats.error_warning++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; + if (bec.txerr > bec.rxerr) + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; + else + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + } + break; + case CAN_STATE_ERROR_ACTIVE: + cf->can_id |= CAN_ERR_CNT; + cf->data[1] = CAN_ERR_CRTL_ACTIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + default: + netdev_err(ndev, "Unexpected state: %d, %s!\n", state, + can_get_state_str(state)); + break; + } + } + + /* Check for Arbitration Lost interrupt */ + if (isr & REG_INT_STAT_ALI) { + isr = REG_INT_STAT_ALI; + netdev_err(ndev, "Arbitration Lost interrupt\n"); + priv->can.can_stats.arbitration_lost++; + if (skb) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = CAN_ERR_LOSTARB_UNSPEC; + } + } + + /* Check for Bus Error interrupt */ + if (isr & REG_INT_STAT_BEI) { + isr = REG_INT_STAT_BEI; + netdev_err(ndev, "Bus Error interrupt\n"); + priv->can.can_stats.bus_error++; + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] = CAN_ERR_PROT_UNSPEC; + cf->data[3] = CAN_ERR_PROT_LOC_UNSPEC; + } + } + + if (skb) + netif_rx(skb); + + regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, isr); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, isr << 16); +} + +static int loongson_canfd_rx_napi(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct loongson_canfd_priv *priv = netdev_priv(ndev); + int work_done = 0, ret = 1; + bool rxbuf_is_empty; + u32 sts; + + rxbuf_is_empty = loongson_canfd_rxbuf_empty(priv); + + while (!rxbuf_is_empty && work_done < quota && ret > 0) { + ret = loongson_canfd_rx(ndev); + work_done++; + rxbuf_is_empty = loongson_canfd_rxbuf_empty(priv); + } + + /* Check for RX FIFO Overflow */ + regmap_read(priv->regmap, LOONGSON_CANFD_STAT, &sts); + if (sts & REG_STAT_DOR) { + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + netdev_info(ndev, "Loongson canfd RX overflow\n"); + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(ndev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + netif_rx(skb); + } + + /* Clear Data Overrun */ + regmap_write(priv->regmap, LOONGSON_CANFD_CMD, REG_CMD_CDO); + } + + if (rxbuf_is_empty && ret != 0) { + if (napi_complete_done(napi, work_done)) { + /* + * Clear and enable RBNEI. It is level-triggered, + * so there is no race condition. + */ + regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_RBNEI); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, + (REG_INT_STAT_RBNEI << 16)); + } + } + + return work_done; +} + +static void loongson_canfd_tx_interrupt(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + enum loongson_canfd_txbuf_sts sts; + u32 buf_id; + + guard(spinlock_irqsave)(&priv->tx_lock); + + while ((buf_id = loongson_canfd_get_txbuf_id(priv, &sts)) < LOONGSON_CANFD_TXBUF_NUM) { + switch (sts) { + case TX_BS_VALID: + stats->tx_bytes += can_get_echo_skb(ndev, buf_id, NULL); + stats->tx_packets++; + break; + case TX_BS_FAIL: + u32 cnt; + /* + * This indicated that retransmit limit has been reached. + * Obviously we should not echo the frame, but also not indicate any + * kind of error. If desired, it was already reported (possible + * multiple times) on each arbitration lost. + */ + regmap_read(priv->regmap, LOONGSON_CANFD_TX_FR_CNT, &cnt); + netdev_warn(ndev, "TXB in FAIL state, TX frame count: %d\n", cnt); + can_free_echo_skb(ndev, buf_id, NULL); + stats->tx_dropped++; + break; + case TX_BS_CANCEL: + /* + * We *could* re-queue the frame, but multiqueue/abort is + * not supported yet anyway. + */ + netdev_warn(ndev, "TXB in CANCEL state\n"); + can_free_echo_skb(ndev, buf_id, NULL); + stats->tx_dropped++; + break; + default: + break; + } + + loongson_canfd_set_txbuf_cmd(ndev, TXT_CMD_SR_CLEAN, buf_id); + } + + /* + * Clear the interrupt again. We do not want to receive again interrupt + * for the buffer already handled. If it is the last finished one then + * it would cause log of spurious interrupt. + */ + regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_TXBHCI); + + /* Check if at least one TX buffer is free */ + if (loongson_canfd_txbuf_freed(priv)) + netif_wake_queue(ndev); +} + +/** + * loongson_canfd_interrupt() - CAN Isr + * @irq: irq number + * @dev_id: device id pointer + * + * This is the Loongson CANFD ISR. It checks for the type of interrupt + * and invokes the corresponding ISR. + * + * Return: + * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise + */ +static irqreturn_t loongson_canfd_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct loongson_canfd_priv *priv = netdev_priv(ndev); + u32 isr, imask; + + for (unsigned int irq_loops = 0; irq_loops < 10000; irq_loops++) { + /* Get the interrupt status */ + regmap_read(priv->regmap, LOONGSON_CANFD_INT_STAT, &isr); + if (!isr) { + if (irq_loops) + return IRQ_HANDLED; + else + return IRQ_NONE; + } + + /* Receive Buffer Not Empty Interrupt */ + if (isr & REG_INT_STAT_RBNEI) { + /* + * Mask RXBNEI the first, then clear interrupt and schedule NAPI. + * Even if another IRQ fires, RBNEI will always be 0 (masked). + */ + regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, REG_INT_STAT_RBNEI); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_RBNEI); + napi_schedule(&priv->napi); + } + + /* TX Buffer HW Command Interrupt */ + if (isr & REG_INT_STAT_TXBHCI) + loongson_canfd_tx_interrupt(ndev); + + /* Error interrupts */ + imask = isr & REG_INT_STAT_ERRORI; + if (imask) { + regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, imask); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, imask); + loongson_canfd_err_interrupt(ndev, isr); + } + + /* Ignore RI, TI, LFI, RFI, BSI */ + } + + netdev_err(ndev, "Intterupt state: 0x%x.\n", isr); + + if (isr & REG_INT_STAT_TXBHCI) { + for (unsigned int i = 0; i < LOONGSON_CANFD_TXBUF_NUM; i++) { + u32 sts = loongson_canfd_get_txbuf_sts(priv, i); + + netdev_err(ndev, "TX buf %d status: 0x%x.\n", i, sts); + } + } + + regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_ENA, REG_INT_ENA_CLR, REG_INT_ENA_CLR); + regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_MASK, + REG_INT_MASK_SET, REG_INT_MASK_SET); + + return IRQ_HANDLED; +} + +/** + * loongson_canfd_chip_stop() - Driver stop routine + * @ndev: Pointer to net_device structure + * + * This is the drivers stop routine. It will disable the interrupts and + * disable the controller. + */ +static void loongson_canfd_chip_stop(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + + /* Disable interrupts and disable CAN */ + regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_ENA, REG_INT_ENA_CLR, REG_INT_ENA_CLR); + regmap_update_bits(priv->regmap, LOONGSON_CANFD_INT_MASK, + REG_INT_MASK_SET, REG_INT_MASK_SET); + regmap_update_bits(priv->regmap, LOONGSON_CANFD_CONF, REG_CONF_ENA, 0); + + priv->can.state = CAN_STATE_STOPPED; +} + +/** + * loongson_canfd_open() - Driver open routine + * @ndev: Pointer to net_device structure + * + * This is the driver open routine. + * Return: 0 on success and failure value on error + */ +static int loongson_canfd_open(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + int ret; + + loongson_canfd_reset(ndev); + + /* Common open */ + ret = open_candev(ndev); + if (ret) { + netdev_warn(ndev, "open_candev failed!\n"); + return ret; + } + + napi_enable(&priv->napi); + ret = request_irq(ndev->irq, loongson_canfd_interrupt, IRQF_SHARED, ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "irq allocation for CAN failed\n"); + goto err_irq; + } + + ret = loongson_canfd_chip_start(ndev); + if (ret < 0) { + netdev_err(ndev, "loongson_canfd_chip_start failed!\n"); + goto err_chip_start; + } + + netdev_info(ndev, "loongson_canfd_device registered\n"); + netif_start_queue(ndev); + + return 0; + +err_chip_start: + free_irq(ndev->irq, ndev); +err_irq: + close_candev(ndev); + return ret; +} + +/** + * loongson_canfd_close() - Driver close routine + * @ndev: Pointer to net_device structure + * + * Return: 0 always + */ +static int loongson_canfd_close(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + loongson_canfd_chip_stop(ndev); + free_irq(ndev->irq, ndev); + close_candev(ndev); + + return 0; +} + +static const struct net_device_ops loongson_canfd_netdev_ops = { + .ndo_open = loongson_canfd_open, + .ndo_stop = loongson_canfd_close, + .ndo_start_xmit = loongson_canfd_start_xmit, +}; + +static const struct ethtool_ops loongson_canfd_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + +static const struct regmap_range loongson_canfd_reg_table_wr_range[] = { + regmap_reg_range(LOONGSON_CANFD_DEVICE_ID, LOONGSON_CANFD_CONF), + regmap_reg_range(LOONGSON_CANFD_CMD, LOONGSON_CANFD_CMD), + regmap_reg_range(LOONGSON_CANFD_INT_STAT, LOONGSON_CANFD_ERL), + regmap_reg_range(LOONGSON_CANFD_CTR_PRES, LOONGSON_CANFD_CTR_PRES), + regmap_reg_range(LOONGSON_CANFD_SSP_CFG, LOONGSON_CANFD_SSP_CFG), + regmap_reg_range(LOONGSON_CANFD_TS, LOONGSON_CANFD_FLT_CTRL), + regmap_reg_range(LOONGSON_CANFD_TX_CMD, LOONGSON_CANFD_TX_DATA_18), +}; + +static const struct regmap_range loongson_canfd_reg_table_rd_range[] = { + regmap_reg_range(LOONGSON_CANFD_DEVICE_ID, LOONGSON_CANFD_STAT), + regmap_reg_range(LOONGSON_CANFD_INT_STAT, LOONGSON_CANFD_BRE), + regmap_reg_range(LOONGSON_CANFD_ERR_CAPT, LOONGSON_CANFD_TX_STAT), + regmap_reg_range(LOONGSON_CANFD_TX_SEL, LOONGSON_CANFD_TX_DATA_18), +}; + +static const struct regmap_access_table loongson_canfd_reg_table_wr = { + .yes_ranges = loongson_canfd_reg_table_wr_range, + .n_yes_ranges = ARRAY_SIZE(loongson_canfd_reg_table_wr_range), +}; + +static const struct regmap_access_table loongson_canfd_reg_table_rd = { + .yes_ranges = loongson_canfd_reg_table_rd_range, + .n_yes_ranges = ARRAY_SIZE(loongson_canfd_reg_table_rd_range), +}; + +static bool loongson_canfd_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LOONGSON_CANFD_MODE: + case LOONGSON_CANFD_CONF: + case LOONGSON_CANFD_STAT: + case LOONGSON_CANFD_INT_STAT: + case LOONGSON_CANFD_INT_ENA: + case LOONGSON_CANFD_INT_MASK: + case LOONGSON_CANFD_ERL: + case LOONGSON_CANFD_FSTAT: + case LOONGSON_CANFD_ERC: + case LOONGSON_CANFD_ERR_CAPT: + case LOONGSON_CANFD_ALC: + case LOONGSON_CANFD_TX_FR_CNT: + case LOONGSON_CANFD_RX_STAT: + case LOONGSON_CANFD_RX_DATA: + case LOONGSON_CANFD_TX_STAT: + case LOONGSON_CANFD_TX_SEL: + return true; + default: + return false; + }; +} + +static const struct regmap_config loongson_cangfd_regmap = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .wr_table = &loongson_canfd_reg_table_wr, + .rd_table = &loongson_canfd_reg_table_rd, + .volatile_reg = loongson_canfd_volatile_reg, + .max_register = LOONGSON_CANFD_TX_DATA_18, + .cache_type = REGCACHE_MAPLE, +}; + +static int loongson_canfd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson_canfd_priv *priv; + struct net_device *ndev; + struct regmap *regmap; + struct resource *res; + void __iomem *base; + u32 clk_rate; + int ret = 0, irq; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &loongson_cangfd_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + device_property_read_u32(dev, "clock-frequency", &clk_rate); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + /* Create a CAN device instance */ + ndev = alloc_candev(sizeof(*priv), LOONGSON_CANFD_TXBUF_NUM); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + spin_lock_init(&priv->tx_lock); + priv->regmap = regmap; + priv->res = res; + + priv->can.clock.freq = clk_rate; + priv->can.bittiming_const = &loongson_canfd_bit_timing; + priv->can.fd.data_bittiming_const = &loongson_canfd_bit_timing_data; + priv->can.fd.tdc_const = &loongson_canfd_tdc_data; + + priv->can.fd.do_set_data_bittiming = loongson_canfd_set_data_bittiming; + priv->can.fd.do_get_auto_tdcv = loongson_canfd_get_auto_tdcv; + priv->can.do_set_mode = loongson_canfd_do_set_mode; + priv->can.do_set_bittiming = loongson_canfd_set_bittiming; + priv->can.do_get_berr_counter = loongson_canfd_get_berr_counter; + + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_ONE_SHOT | + CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD | + CAN_CTRLMODE_PRESUME_ACK | CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_TDC_AUTO | CAN_CTRLMODE_CC_LEN8_DLC; + + ndev->irq = irq; + ndev->flags |= IFF_ECHO; /* We support local echo */ + platform_set_drvdata(pdev, ndev); + ndev->netdev_ops = &loongson_canfd_netdev_ops; + ndev->ethtool_ops = &loongson_canfd_ethtool_ops; + SET_NETDEV_DEV(ndev, dev); + + netif_napi_add(ndev, &priv->napi, loongson_canfd_rx_napi); + + ret = register_candev(ndev); + if (ret) { + dev_err(dev, "register_candev failed with %d\n", ret); + goto err_candev_free; + } + + return 0; + +err_candev_free: + netif_napi_del(&priv->napi); + free_candev(ndev); + return ret; +} + +static void loongson_canfd_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct loongson_canfd_priv *priv = netdev_priv(ndev); + + unregister_candev(ndev); + netif_napi_del(&priv->napi); + free_candev(ndev); +} + +static const struct acpi_device_id loongson_canfd_acpi_match[] = { + { "LOON0015" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_canfd_acpi_match); + +static struct platform_driver loongson_canfd_driver = { + .probe = loongson_canfd_probe, + .remove = loongson_canfd_remove, + .driver = { + .name = DEV_NAME, + .acpi_match_table = loongson_canfd_acpi_match, + }, +}; +module_platform_driver(loongson_canfd_driver); + +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_DESCRIPTION("Loongson CAN-FD Controller driver"); +MODULE_LICENSE("GPL"); From 8eef23a6f872c2d3b83e76245bd8a541f579d967 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 30 Jun 2026 11:01:41 +0800 Subject: [PATCH 223/521] FROMLIST: can: loongson_canfd: Add RXDMA support Add optional DMA support for RX path using the Loongson APB CMC DMA engine. When a DMA channel is successfully requested, the driver: - Uses DMA cyclic transfers to write incoming CAN frames directly to a coherent DMA buffer - Replaces RXBNEI (RX buffer not empty interrupt) with DMADI (DMA done interrupt) - Dynamically switches between DMA and PIO modes based on channel availability This significantly reduces CPU intervention under high RX load, especially beneficial for CAN FD at higher data rates. Co-developed-by: Bingxiong Li Signed-off-by: Bingxiong Li Signed-off-by: Binbin Zhou Link: https://lore.kernel.org/loongarch/cover.1782787997.git.zhoubinbin@loongson.cn/ Signed-off-by: Mingcong Bai --- drivers/net/can/Kconfig | 2 +- drivers/net/can/loongson_canfd.c | 202 ++++++++++++++++++++++++++++--- 2 files changed, 185 insertions(+), 19 deletions(-) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 55c40b0654fbe8..f367517ede0d13 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -191,7 +191,7 @@ config CAN_KVASER_PCIEFD config CAN_LOONGSON_CANFD tristate "Loongson CAN-FD controller" - depends on HAS_IOMEM + depends on HAS_IOMEM && (LOONGSON2_APB_CMC_DMA || COMPILE_TEST) select REGMAP_MMIO help This is a canfd driver switch for the Loongson platform, diff --git a/drivers/net/can/loongson_canfd.c b/drivers/net/can/loongson_canfd.c index 1c7381129c2b61..a4b5d9e5067655 100644 --- a/drivers/net/can/loongson_canfd.c +++ b/drivers/net/can/loongson_canfd.c @@ -6,10 +6,14 @@ */ #include +#include #include #include #include #include +#include +#include +#include #include #include #include @@ -339,8 +343,10 @@ #define DEV_NAME "loongson_canfd" #define LOONGSON_CANFD_ID 0xBABE #define LOONGSON_CANFD_DW_BYTE 4 +#define LOONGSON_CANFD_RXBUF_SZ SZ_1K #define LOONGSON_CANFD_TXBUF_NUM 8 #define LOONGSON_CANFD_MAX_RTXTH 0xf +#define LOONGSON_CANFD_RXDMA_NUM (LOONGSON_CANFD_RXBUF_SZ / DMA_SLAVE_BUSWIDTH_4_BYTES) /** * struct loongson_canfd_priv - This definition define CAN driver instance @@ -348,14 +354,26 @@ * @napi: NAPI structure * @regmap: regmap of the CAN device * @res: Pointer to the CAN device respurce + * @rx_ch: CAN DMA rx channel + * @rx_dma_buf: CAN DMA rx buffer bus address + * @rx_buf: CAN DMA rx buffer cpu address + * @last_res: Last rx data in DMA route * @tx_lock: Lock for synchronizing TX interrupt handling + * @get_rx_data: Callback of reading CAN rx data + * @get_rxbuf_empty: Callback of gets the RX buffer is empty in dma mode */ struct loongson_canfd_priv { struct can_priv can; /* must be first member! */ struct napi_struct napi; struct regmap *regmap; struct resource *res; + struct dma_chan *rx_ch; + dma_addr_t rx_dma_buf; /* dma rx buffer bus address */ + unsigned int *rx_buf; /* dma rx buffer cpu address */ + u16 last_res; spinlock_t tx_lock; /* protect the sending queue */ + u32 (*get_rx_data)(struct loongson_canfd_priv *priv); + bool (*get_rxbuf_empty)(struct loongson_canfd_priv *priv); }; /** @@ -536,13 +554,139 @@ static void loongson_canfd_set_txbuf_cmd(struct net_device *ndev, } /** - * loongson_canfd_rxbuf_empty() - Gets the RX buffer is empty + * loongson_canfd_get_rxdata_in_dma() - Reading RX data in DMA mode + * @priv: Pointer to private data + * + * Return: The CANFD RX data. + */ +static u32 loongson_canfd_get_rxdata_in_dma(struct loongson_canfd_priv *priv) +{ + u32 data = 0; + + data = priv->rx_buf[LOONGSON_CANFD_RXDMA_NUM - priv->last_res--]; + if (!priv->last_res) + priv->last_res = LOONGSON_CANFD_RXDMA_NUM; + + return data; +} + +/** + * loongson_canfd_get_rxbuf_empty_in_dma() - Gets the RX buffer is empty in dma mode * @priv: Pointer to private data * * Return: True - RX buffer is empty. * False - RX buffer is processing */ -static bool loongson_canfd_rxbuf_empty(struct loongson_canfd_priv *priv) +static bool loongson_canfd_get_rxbuf_empty_in_dma(struct loongson_canfd_priv *priv) +{ + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(priv->rx_ch, priv->rx_ch->cookie, &state); + if (status != DMA_IN_PROGRESS) + return true; + + return priv->last_res == (state.residue / DMA_SLAVE_BUSWIDTH_4_BYTES); +} + +static void loongson_canfd_rxdma_free(struct loongson_canfd_priv *priv, struct device *dev) +{ + dma_free_coherent(dev, LOONGSON_CANFD_RXBUF_SZ, priv->rx_buf, priv->rx_dma_buf); +} + +static void loongson_canfd_rxdma_remove(struct loongson_canfd_priv *priv, struct device *dev) +{ + if (!priv->rx_ch) + return; + + dmaengine_terminate_sync(priv->rx_ch); + loongson_canfd_rxdma_free(priv, dev); + dma_release_channel(priv->rx_ch); +} + +/** + * loongson_canfd_rxdma_init() - Loongson canfd RXDMA initialization + * @ndev: Pointer to net_device structure + * + * Return: The number of messages in the receive buffer + */ +static int loongson_canfd_rxdma_init(struct net_device *ndev) +{ + struct loongson_canfd_priv *priv = netdev_priv(ndev); + struct dma_async_tx_descriptor *desc = NULL; + struct device *dev = ndev->dev.parent; + struct dma_slave_config config; + int ret; + + if (!priv->rx_ch) + return -EINVAL; + + priv->rx_buf = dma_alloc_coherent(dev, LOONGSON_CANFD_RXBUF_SZ, + &priv->rx_dma_buf, GFP_KERNEL); + if (!priv->rx_buf) + return -ENOMEM; + + /* Configure DMA channel */ + memset(&config, 0, sizeof(config)); + config.src_addr = priv->res->start + LOONGSON_CANFD_RX_DATA; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + ret = dmaengine_slave_config(priv->rx_ch, &config); + if (ret < 0) { + netdev_err(ndev, "Loongson canfd rxdma channel config failed\n"); + goto err_config; + } + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(priv->rx_ch, priv->rx_dma_buf, + LOONGSON_CANFD_RXBUF_SZ, LOONGSON_CANFD_RXBUF_SZ, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!desc) { + netdev_err(ndev, "Loongson canfd rxdma cyclic transaction failed\n"); + ret = -EBUSY; + goto err_config; + } + + /* Push current dma transaction in the pending queue */ + ret = dma_submit_error(dmaengine_submit(desc)); + if (ret) { + dmaengine_terminate_sync(priv->rx_ch); + goto err_config; + } + + /* Issue pending DMA requests */ + dma_async_issue_pending(priv->rx_ch); + + return 0; + +err_config: + loongson_canfd_rxdma_free(priv, dev); + return ret; +} + +/** + * loongson_canfd_get_rxdata_in_poll() - Reading RX data in poll mode + * @priv: Pointer to private data + * + * Return: The CANFD RX data. + */ +static u32 loongson_canfd_get_rxdata_in_poll(struct loongson_canfd_priv *priv) +{ + u32 data; + + regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data); + + return data; +} + +/** + * loongson_canfd_get_rxbuf_empty_in_poll() - Gets the RX buffer is empty in poll mode + * @priv: Pointer to private data + * + * Return: True - RX buffer is empty. + * False - RX buffer is processing + */ +static bool loongson_canfd_get_rxbuf_empty_in_poll(struct loongson_canfd_priv *priv) { return !!regmap_test_bits(priv->regmap, LOONGSON_CANFD_RX_STAT, REG_RX_STAT_RXE); } @@ -767,8 +911,8 @@ static int loongson_canfd_chip_start(struct net_device *ndev) loongson_canfd_set_conf_mode(priv); /* Configure interrupts */ - int_ena = REG_INT_STAT_RBNEI | REG_INT_STAT_TXBHCI | - REG_INT_STAT_EWLI | REG_INT_STAT_FCSI; + int_ena = REG_INT_STAT_TXBHCI | REG_INT_STAT_EWLI | REG_INT_STAT_FCSI; + int_ena |= priv->rx_ch ? REG_INT_STAT_DMADI : REG_INT_STAT_RBNEI; /* Bus error reporting */ if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) @@ -961,7 +1105,7 @@ static netdev_tx_t loongson_canfd_start_xmit(struct sk_buff *skb, struct net_dev static void loongson_canfd_read_rx_frame(struct loongson_canfd_priv *priv, struct canfd_frame *cf, u32 meta0, u32 meta1) { - u32 data, i, wc, len; + u32 i, wc, len; /* Extended Identifier Type */ if (meta0 & REG_FRAME_META0_XDT) @@ -999,10 +1143,10 @@ static void loongson_canfd_read_rx_frame(struct loongson_canfd_priv *priv, struc /* Data */ for (i = 0; i < len; i += LOONGSON_CANFD_DW_BYTE) - regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, (u32 *)(cf->data + i)); + *(u32 *)(cf->data + i) = priv->get_rx_data(priv); while (unlikely(i < wc * LOONGSON_CANFD_DW_BYTE)) { - regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &data); + priv->get_rx_data(priv); i += LOONGSON_CANFD_DW_BYTE; } } @@ -1025,8 +1169,8 @@ static int loongson_canfd_rx(struct net_device *ndev) struct sk_buff *skb; u32 meta0, meta1; - regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta0); - regmap_read(priv->regmap, LOONGSON_CANFD_RX_DATA, &meta1); + meta0 = priv->get_rx_data(priv); + meta1 = priv->get_rx_data(priv); /* Number of characters received */ if (!FIELD_GET(REG_FRAME_META1_RWCNT, meta1)) @@ -1254,16 +1398,17 @@ static int loongson_canfd_rx_napi(struct napi_struct *napi, int quota) { struct net_device *ndev = napi->dev; struct loongson_canfd_priv *priv = netdev_priv(ndev); + int int_ena = priv->rx_ch ? REG_INT_STAT_DMADI : REG_INT_STAT_RBNEI; int work_done = 0, ret = 1; bool rxbuf_is_empty; u32 sts; - rxbuf_is_empty = loongson_canfd_rxbuf_empty(priv); + rxbuf_is_empty = priv->get_rxbuf_empty(priv); while (!rxbuf_is_empty && work_done < quota && ret > 0) { ret = loongson_canfd_rx(ndev); work_done++; - rxbuf_is_empty = loongson_canfd_rxbuf_empty(priv); + rxbuf_is_empty = priv->get_rxbuf_empty(priv); } /* Check for RX FIFO Overflow */ @@ -1291,12 +1436,11 @@ static int loongson_canfd_rx_napi(struct napi_struct *napi, int quota) if (rxbuf_is_empty && ret != 0) { if (napi_complete_done(napi, work_done)) { /* - * Clear and enable RBNEI. It is level-triggered, + * Clear and enable RBNEI/DMADI. It is level-triggered, so * so there is no race condition. */ - regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_RBNEI); - regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, - (REG_INT_STAT_RBNEI << 16)); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, int_ena); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, (int_ena << 16)); } } @@ -1387,13 +1531,14 @@ static irqreturn_t loongson_canfd_interrupt(int irq, void *dev_id) } /* Receive Buffer Not Empty Interrupt */ - if (isr & REG_INT_STAT_RBNEI) { + imask = priv->rx_ch ? REG_INT_STAT_DMADI : REG_INT_STAT_RBNEI; + if (isr & imask) { /* * Mask RXBNEI the first, then clear interrupt and schedule NAPI. * Even if another IRQ fires, RBNEI will always be 0 (masked). */ - regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, REG_INT_STAT_RBNEI); - regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, REG_INT_STAT_RBNEI); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_MASK, imask); + regmap_write(priv->regmap, LOONGSON_CANFD_INT_STAT, imask); napi_schedule(&priv->napi); } @@ -1646,6 +1791,24 @@ static int loongson_canfd_probe(struct platform_device *pdev) ndev->ethtool_ops = &loongson_canfd_ethtool_ops; SET_NETDEV_DEV(ndev, dev); + priv->get_rx_data = loongson_canfd_get_rxdata_in_poll; + priv->get_rxbuf_empty = loongson_canfd_get_rxbuf_empty_in_poll; + + priv->rx_ch = dma_request_chan(dev, "rx"); + if (PTR_ERR(priv->rx_ch) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (IS_ERR(priv->rx_ch)) { + dev_warn(dev, "Fall back in poll mode for any non-deferral error.\n"); + priv->rx_ch = NULL; + } + + ret = loongson_canfd_rxdma_init(ndev); + if (!ret) { + priv->get_rx_data = loongson_canfd_get_rxdata_in_dma; + priv->get_rxbuf_empty = loongson_canfd_get_rxbuf_empty_in_dma; + priv->last_res = LOONGSON_CANFD_RXDMA_NUM; + } netif_napi_add(ndev, &priv->napi, loongson_canfd_rx_napi); ret = register_candev(ndev); @@ -1658,6 +1821,7 @@ static int loongson_canfd_probe(struct platform_device *pdev) err_candev_free: netif_napi_del(&priv->napi); + loongson_canfd_rxdma_remove(priv, &pdev->dev); free_candev(ndev); return ret; } @@ -1669,6 +1833,7 @@ static void loongson_canfd_remove(struct platform_device *pdev) unregister_candev(ndev); netif_napi_del(&priv->napi); + loongson_canfd_rxdma_remove(priv, &pdev->dev); free_candev(ndev); } @@ -1688,6 +1853,7 @@ static struct platform_driver loongson_canfd_driver = { }; module_platform_driver(loongson_canfd_driver); +MODULE_SOFTDEP("pre: loongson2-apb-cmc-dma"); MODULE_AUTHOR("Loongson Technology Corporation Limited"); MODULE_DESCRIPTION("Loongson CAN-FD Controller driver"); MODULE_LICENSE("GPL"); From 35c72b0a98a382a1212fe6b64f1b4bfb6e3c08bc Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Tue, 30 Jun 2026 05:05:28 +0800 Subject: [PATCH 224/521] FROMLIST: gpio: loongson-64bit: Add back the support for gsi_idx_map This patch adds back the support for gsi_idx_map, which is used in the ACPI DSDT table to describe the mapping between the GPIO line number to the index of the interrupt number in the declared interrupt resources. This property was removed in Loongson CPU Universal Specification for Interface Between PC/Server System Firmware and Kernel v4.1 in November, 2023, but still in use in firmwares released this year. A sample of an affected DSDT entry from a 3C6000 board I'm currently using is: Device (GPO1) { Name (_HID, "LOON000F") Name (_CRS, ResourceTemplate () { QWordMemory ( // Omitted, not related ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000010, 0x00000011, 0x00000012, 0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, } Name (_DSD, Package (0x02) { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") Package (0x03) { Package (0x02) { "gpio_base", 0x50 } // Ignored by the driver Package (0x02) { "ngpios", 0x20 } Package (0x02) { "gsi_idx_map", Package (0x20) { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, }} } } } As can be seen in the DSDT entry, the mapping is essential for obtaining the IRQ number from a GPIO line number. Otherwise, when IRQ is requested for the line numbers largers than 7, it will fail with -ENXIO. The code in this patch is mostly picked from the version 5 of Yinbo's original patch. Fixes: 7944d3b7fe86 ("gpio: loongson: add gpio driver support") Signed-off-by: Miao Wang Link: https://lore.kernel.org/linux-gpio/20260630-loongson-gpio-v1-1-576908831fa0@gmail.com/ Signed-off-by: Mingcong Bai --- drivers/gpio/gpio-loongson-64bit.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index 0fdf15faa344d2..bff7d70e7470ea 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -46,6 +46,8 @@ struct loongson_gpio_chip { spinlock_t lock; void __iomem *reg_base; const struct loongson_gpio_chip_data *chip_data; + u16 *gsi_idx_map; + int mapsize; }; static inline struct loongson_gpio_chip *to_loongson_gpio_chip(struct gpio_chip *chip) @@ -145,6 +147,12 @@ static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) writeb(1, lgpio->reg_base + lgpio->chip_data->inten_offset + offset); } + if (lgpio->gsi_idx_map != NULL) { + if (offset >= lgpio->mapsize) + return -EINVAL; + offset = lgpio->gsi_idx_map[offset]; + } + return platform_get_irq(pdev, offset); } @@ -326,6 +334,23 @@ static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio lgpio->chip.gc.to_irq = loongson_gpio_to_irq; } + lgpio->mapsize = device_property_read_u16_array(&pdev->dev, "gsi_idx_map", NULL, 0); + if (lgpio->mapsize > 0) { + lgpio->gsi_idx_map = devm_kcalloc(&pdev->dev, lgpio->mapsize, + sizeof(*lgpio->gsi_idx_map), GFP_KERNEL); + if (!lgpio->gsi_idx_map) + return -ENOMEM; + + ret = device_property_read_u16_array(&pdev->dev, "gsi_idx_map", + lgpio->gsi_idx_map, lgpio->mapsize); + if (ret != 0) + return dev_err_probe(&pdev->dev, ret, "failed to read gsi_idx_map\n"); + dev_warn(&pdev->dev, "gsi_idx_map property is deprecated, consider upgrading your firmware\n"); + } else { + lgpio->gsi_idx_map = NULL; + lgpio->mapsize = 0; + } + return devm_gpiochip_add_data(&pdev->dev, &lgpio->chip.gc, lgpio); } From f832b9c44eabe1451ebd3bcef8ff8297e3819543 Mon Sep 17 00:00:00 2001 From: Rong Bao Date: Sun, 5 Jul 2026 17:36:14 +0800 Subject: [PATCH 225/521] FROMLIST: loongarch: retrieve CPU package ID from PPTT when available Currently, the LoongArch CPU topology initialization code calculates each core's package ID by dividing its physical ID by loongson_sysconf.cores_per_package. This relies on the assumption that cores_per_package counts in the same domain as physical IDs. On Loongson 3B6000 (XB612B0V_1.2), cores_per_package matches the visible core count -- 24 in this case. However, the physical IDs range from 0 to 31 in a noncontinuous fashion: $ cat /proc/cpuinfo | grep -i -F 'global_id' global_id : 0 global_id : 1 global_id : 4 global_id : 5 global_id : 6 global_id : 7 global_id : 8 global_id : 9 global_id : 10 global_id : 11 global_id : 14 global_id : 15 global_id : 16 global_id : 17 global_id : 20 global_id : 21 global_id : 22 global_id : 23 global_id : 26 global_id : 27 global_id : 28 global_id : 29 global_id : 30 global_id : 31 Retrieve the exact package ID from ACPI PPTT when available, in the same style as retrieving the core ID and thread ID in parse_acpi_topology(). Use this information in loongson_init_secondary() when PPTT readout is successful. The original division logic is kept as a fallback. Meanwhile, since some code paths like loongson3_cpufreq expect a continuous integer sequence of package IDs in [0, MAX_PACKAGES) when retrieving from cpu_data[], we also canonicalize the package ID to be filled in parse_acpi_topology() to meet such an expectation. Cc: stable@vger.kernel.org Co-developed-by: Xi Ruoyao Signed-off-by: Xi Ruoyao Signed-off-by: Rong Bao Link: https://lore.kernel.org/loongarch/20260705093624.1079988-1-rong.bao@csmantle.top/ Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/acpi.c | 28 +++++++++++++++++++++++++++- arch/loongarch/kernel/smp.c | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 8f650c9ffecdec..9ad25c642dbc50 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -202,9 +202,12 @@ static void __init acpi_process_madt(void) int pptt_enabled; +static int acpi_package_ids[MAX_PACKAGES]; +static int acpi_nr_packages; + int __init parse_acpi_topology(void) { - int cpu, topology_id; + int cpu, topology_id, i; for_each_possible_cpu(cpu) { topology_id = find_acpi_cpu_topology(cpu, 0); @@ -222,6 +225,29 @@ int __init parse_acpi_topology(void) cpu_data[cpu].core = topology_id; } + + topology_id = find_acpi_cpu_topology_package(cpu); + if (topology_id < 0) { + pr_warn("Invalid BIOS PPTT\n"); + return -ENOENT; + } + + for (i = 0; i < acpi_nr_packages; i++) + if (acpi_package_ids[i] == topology_id) + break; + + if (i == acpi_nr_packages) + acpi_package_ids[acpi_nr_packages++] = topology_id; + + cpu_data[cpu].package = topology_id; + } + + for_each_possible_cpu(cpu) { + for (int i = 0; i < acpi_nr_packages; i++) + if (cpu_data[cpu].package == acpi_package_ids[i]) { + cpu_data[cpu].package = i; + break; + } } pptt_enabled = 1; diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index b077b2aa1ab370..401fc236aac27b 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -425,7 +425,7 @@ void loongson_init_secondary(void) numa_add_cpu(cpu); #endif per_cpu(cpu_state, cpu) = CPU_ONLINE; - cpu_data[cpu].package = + cpu_data[cpu].package = pptt_enabled ? cpu_data[cpu].package : cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; cpu_data[cpu].core = pptt_enabled ? cpu_data[cpu].core : cpu_logical_map(cpu) % loongson_sysconf.cores_per_package; From 8f98e8acc2eddd44d396bc8695ac28583e3d0986 Mon Sep 17 00:00:00 2001 From: Zixing Liu Date: Wed, 22 Jul 2026 12:44:50 +0800 Subject: [PATCH 226/521] FROMLIST: platform/loongarch: laptop: explicitly reset the bl_powered state when resuming On EAECIS NL60R with EC firmware version 1.11, resuming from S3 has a very high chance (>= 90%) of causing the EC to lose the previous backlight power state. When this happens, the laptop resumes normally from S3, but the backlight remains off (when shining on the screen with a flash light, we can see the screen contents are updating normally). Since there is no generic way to query the EC's backlight state on Loongson laptop platforms, assume the worst-case scenario and restart the backlight power inside the kernel each time the system resumes. Cc: stable@vger.kernel.org Fixes: 53c762b47f72 ("platform/loongarch: laptop: Add backlight power control support") Signed-off-by: Zixing Liu Link: https://lore.kernel.org/loongarch/20260722044457.1333404-1-liushuyu@aosc.io/ Signed-off-by: Mingcong Bai --- drivers/platform/loongarch/loongson-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/loongarch/loongson-laptop.c b/drivers/platform/loongarch/loongson-laptop.c index 61b18ac206c9ee..742a585b0647ce 100644 --- a/drivers/platform/loongarch/loongson-laptop.c +++ b/drivers/platform/loongarch/loongson-laptop.c @@ -200,6 +200,7 @@ static int loongson_hotkey_resume(struct device *dev) bd = backlight_device_get_by_type(BACKLIGHT_PLATFORM); if (bd) { + bl_powered = false; loongson_laptop_backlight_update(bd) ? pr_warn("Loongson_backlight: resume brightness failed") : pr_info("Loongson_backlight: resume brightness %d\n", bd->props.brightness); From 1278d9ca2b297a4530b64f4e9a023ec7a5518e10 Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Tue, 9 Jun 2026 21:00:18 +0800 Subject: [PATCH 227/521] FROMLIST: dt-bindings: riscv: clarify Svadu boot-time behavior Clarify that systems which advertise only Svadu have hardware PTE A/D updating enabled at boot, while systems advertising both Svade and Svadu must enable Svadu explicitly with SBI FWFT. Signed-off-by: Yunhui Cui Reviewed-by: Qingwei Hu Link: https://lore.kernel.org/r/20260609130021.99899-2-cuiyunhui@bytedance.com Signed-off-by: Han Gao --- Documentation/devicetree/bindings/riscv/extensions.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml index 2b0a8a93bb2144..b09888e9988de6 100644 --- a/Documentation/devicetree/bindings/riscv/extensions.yaml +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml @@ -294,10 +294,10 @@ properties: of the PTE A/D bits or page faults when they need updated. 2) Only Svade present in DT => Supervisor must assume Svade to be always enabled. - 3) Only Svadu present in DT => Supervisor must assume Svadu to be - always enabled. + 3) Only Svadu present in DT => Supervisor must assume Svadu is + enabled at boot. 4) Both Svade and Svadu present in DT => Supervisor must assume - Svadu turned-off at boot time. To use Svadu, supervisor must + Svadu is disabled at boot time. To use Svadu, supervisor must explicitly enable it using the SBI FWFT extension. - const: svadu From ef62c50e10f2ee452e16a8a6269094d16d70f2dc Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Tue, 9 Jun 2026 21:00:19 +0800 Subject: [PATCH 228/521] FROMLIST: riscv: track effective hardware PTE A/D updating Track whether hardware PTE A/D updating is effectively active separately from Svadu discovery. Enable FWFT on all online harts before advertising hardware A/D updating globally, and require secondary harts to enable it before they are marked online. Signed-off-by: Yunhui Cui Reviewed-by: Qingwei Hu Link: https://lore.kernel.org/r/20260609130021.99899-3-cuiyunhui@bytedance.com Signed-off-by: Han Gao --- arch/riscv/include/asm/cpufeature.h | 8 +++ arch/riscv/kernel/cpufeature.c | 89 +++++++++++++++++++++++++++-- arch/riscv/kernel/smpboot.c | 4 ++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h index 739fcc84bf7b28..ba3d74f6006a65 100644 --- a/arch/riscv/include/asm/cpufeature.h +++ b/arch/riscv/include/asm/cpufeature.h @@ -128,6 +128,14 @@ struct riscv_isa_ext_data { extern const struct riscv_isa_ext_data riscv_isa_ext[]; extern const size_t riscv_isa_ext_count; extern bool riscv_isa_fallback; +DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating); + +static __always_inline bool riscv_has_hw_pte_ad_updating(void) +{ + return static_branch_unlikely(&riscv_hw_pte_ad_updating); +} + +int riscv_enable_hw_pte_ad_updating(void); unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap); static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext) diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index f46aa5602d74d3..f144797fa2dc84 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,9 @@ static bool any_cpu_has_zicboz; static bool any_cpu_has_zicbop; static bool any_cpu_has_zicbom; +DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating); +EXPORT_SYMBOL_GPL(riscv_hw_pte_ad_updating); +static bool riscv_hw_pte_ad_updating_requires_fwft __read_mostly; unsigned long elf_hwcap __read_mostly; @@ -287,15 +291,88 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data, return -EPROBE_DEFER; } -static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data, - const unsigned long *isa_bitmap) +static void riscv_set_hw_pte_ad_updating(void) +{ + static_branch_enable(&riscv_hw_pte_ad_updating); +} + +static int riscv_enable_local_hw_pte_ad_updating(void) +{ + return sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0); +} + +static int riscv_set_online_hw_pte_ad_updating(bool enable) +{ + return sbi_fwft_set_online_cpus(SBI_FWFT_PTE_AD_HW_UPDATING, + enable, 0); +} + +int riscv_enable_hw_pte_ad_updating(void) +{ + unsigned int cpu; + int ret; + + if (!riscv_has_hw_pte_ad_updating() || + !riscv_hw_pte_ad_updating_requires_fwft) + return 0; + + cpu = smp_processor_id(); + ret = riscv_enable_local_hw_pte_ad_updating(); + if (ret) + pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n", + cpu, ret); + + return ret; +} + +static void riscv_disable_hw_pte_ad_updating(int error) +{ + int ret; + + riscv_hw_pte_ad_updating_requires_fwft = false; + if (error != -EOPNOTSUPP) + pr_err("Failed to enable hardware PTE A/D updating: %d\n", + error); + + ret = riscv_set_online_hw_pte_ad_updating(false); + if (ret && ret != -EOPNOTSUPP) + pr_err("Failed to rollback hardware PTE A/D updating: %d\n", + ret); + + pr_info("riscv: leave PTE A/D updates software-managed (%d)\n", + error); +} + +static int __init riscv_hw_pte_ad_updating_init(void) { - /* SVADE has already been detected, use SVADE only */ - if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE)) - return -EOPNOTSUPP; + bool has_svade, has_svadu; + int ret; + has_svade = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADE); + has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU); + + if (!has_svadu) + return 0; + + if (has_svade) { + riscv_hw_pte_ad_updating_requires_fwft = true; + ret = riscv_set_online_hw_pte_ad_updating(true); + if (ret) { + riscv_disable_hw_pte_ad_updating(ret); + return 0; + } + } + + /* + * At this point hardware PTE A/D updating is active for all online + * harts, either from boot or from the FWFT setup above. Later harts + * must do the same in secondary startup before they are marked online. + */ + riscv_set_hw_pte_ad_updating(); + pr_debug("riscv: hardware PTE A/D updating enabled\n"); return 0; } +arch_initcall(riscv_hw_pte_ad_updating_init); static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data, const unsigned long *isa_bitmap) @@ -584,7 +661,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = { __RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts), __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC), __RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE), - __RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate), + __RISCV_ISA_EXT_DATA(svadu, RISCV_ISA_EXT_SVADU), __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL), __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT), __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT), diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 8b628580fe118e..4fe62f96bcca2a 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -221,6 +222,9 @@ asmlinkage __visible void smp_callin(void) struct mm_struct *mm = &init_mm; unsigned int curr_cpuid = smp_processor_id(); + if (riscv_enable_hw_pte_ad_updating()) + return; + if (has_vector()) { /* * Return as early as possible so the hart with a mismatching From c55e1a25953cf44463541f04df18e3baa12b9440 Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Tue, 9 Jun 2026 21:00:20 +0800 Subject: [PATCH 229/521] FROMLIST: riscv: preserve A/D and soft-dirty state across PTE updates Use cmpxchg-based PTE updates so software permission changes do not lose concurrent A/D updates from hardware. Preserve soft-dirty state as well, since RISC-V marks PTEs dirty and soft-dirty together. Signed-off-by: Yunhui Cui Reviewed-by: Qingwei Hu Link: https://lore.kernel.org/r/20260609130021.99899-4-cuiyunhui@bytedance.com Signed-off-by: Han Gao --- arch/riscv/include/asm/pgtable.h | 27 +++++++++---- arch/riscv/mm/pgtable.c | 68 ++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 18 deletions(-) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 5d5756bda82e3d..02286b48dc4712 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -678,15 +678,21 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep) { - pte_t read_pte = READ_ONCE(*ptep); + pte_t old_pte; + pte_t pte; /* * ptep_set_wrprotect can be called for shadow stack ranges too. * shadow stack memory is XWR = 010 and thus clearing _PAGE_WRITE will lead to * encoding 000b which is wrong encoding with V = 1. This should lead to page fault * but we dont want this wrong configuration to be set in page tables. */ - atomic_long_set((atomic_long_t *)ptep, - ((pte_val(read_pte) & ~(unsigned long)_PAGE_WRITE) | _PAGE_READ)); + pte = READ_ONCE(*ptep); + do { + old_pte = pte; + pte = pte_wrprotect(pte); + pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep), pte_val(old_pte), + pte_val(pte)); + } while (pte_val(pte) != pte_val(old_pte)); } #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH @@ -742,14 +748,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot) #define pgprot_dmacoherent pgprot_writecombine /* - * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By - * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in - * DT. + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits + * need to be set. The core MM code only cares whether hardware updating of + * the accessed/dirty state is currently active. */ #define arch_has_hw_pte_young arch_has_hw_pte_young static inline bool arch_has_hw_pte_young(void) { - return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU); + return riscv_has_hw_pte_ad_updating(); } /* @@ -1040,6 +1046,13 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm, ptep_set_wrprotect(mm, address, (pte_t *)pmdp); } +#define __HAVE_ARCH_PUDP_SET_WRPROTECT +static inline void pudp_set_wrprotect(struct mm_struct *mm, + unsigned long address, pud_t *pudp) +{ + ptep_set_wrprotect(mm, address, (pte_t *)pudp); +} + #define pmdp_establish pmdp_establish static inline pmd_t pmdp_establish(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp, pmd_t pmd) diff --git a/arch/riscv/mm/pgtable.c b/arch/riscv/mm/pgtable.c index 9c4427d0b1874e..98eed19ea70dee 100644 --- a/arch/riscv/mm/pgtable.c +++ b/arch/riscv/mm/pgtable.c @@ -5,23 +5,55 @@ #include #include +#define RISCV_PTE_ACCESS_FLAG_MASK (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC | \ + _PAGE_ACCESSED | _PAGE_DIRTY | \ + _PAGE_SOFT_DIRTY) + +static inline unsigned long riscv_pte_access_flags(unsigned long cur, + unsigned long entry) +{ + unsigned long pteval; + unsigned long preserved_flags; + + preserved_flags = _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_SOFT_DIRTY; + pteval = cur & ~RISCV_PTE_ACCESS_FLAG_MASK; + pteval |= entry & (RISCV_PTE_ACCESS_FLAG_MASK & ~preserved_flags); + pteval |= (cur | entry) & preserved_flags; + + return pteval; +} + int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t entry, int dirty) { + unsigned long old_pteval; + unsigned long new_pteval; + unsigned long prev_pteval; + bool changed; + + old_pteval = pte_val(ptep_get(ptep)); + do { + new_pteval = riscv_pte_access_flags(old_pteval, pte_val(entry)); + if (new_pteval == old_pteval) + break; + + prev_pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, + new_pteval); + if (prev_pteval == old_pteval) + break; + + old_pteval = prev_pteval; + } while (1); + + changed = old_pteval != new_pteval; if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC)) { - if (!pte_same(ptep_get(ptep), entry)) { - __set_pte_at(vma->vm_mm, ptep, entry); - /* Here only not svadu is impacted */ + if (changed) flush_tlb_page(vma, address); - return true; - } - return false; + return changed; } - if (!pte_same(ptep_get(ptep), entry)) - __set_pte_at(vma->vm_mm, ptep, entry); /* * update_mmu_cache will unconditionally execute, handling both * the case that the PTE changed and the spurious fault case. @@ -32,9 +64,23 @@ int ptep_set_access_flags(struct vm_area_struct *vma, bool ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) { - if (!pte_young(ptep_get(ptep))) - return false; - return test_and_clear_bit(_PAGE_ACCESSED_OFFSET, &pte_val(*ptep)); + unsigned long old_pteval; + unsigned long new_pteval; + unsigned long prev_pteval; + + old_pteval = pte_val(ptep_get(ptep)); + do { + if (!(old_pteval & _PAGE_ACCESSED)) + return false; + + new_pteval = pte_val(pte_mkold(__pte(old_pteval))); + prev_pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, + new_pteval); + if (prev_pteval == old_pteval) + return true; + + old_pteval = prev_pteval; + } while (1); } EXPORT_SYMBOL_GPL(ptep_test_and_clear_young); From abe7b83f16c5ff3b04ee447541170759e5055c10 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Wed, 10 Jun 2026 12:34:32 +0000 Subject: [PATCH 230/521] FROMLIST: riscv: dts: spacemit: k3: Enable SD card support Add the device tree node of SDHCI-0 controller which found on CoM260-IFX board, it's currenty used for Micro SD card slot, and one GPIO pin is configured as card insertion/removal detection pin, the slot use 4-bit data bus, and up support to SDR104 speed mode. Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260610-07-k3-enable-sd-v1-1-196ae29bd091@kernel.org Signed-off-by: Han Gao --- .../riscv/boot/dts/spacemit/k3-com260-ifx.dts | 18 ++++++++ arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 44 +++++++++++++++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 13 ++++++ 3 files changed, 75 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts b/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts index 238bb03d0e9e23..e7f76cd24b9032 100644 --- a/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts @@ -19,3 +19,21 @@ stdout-path = "serial0:115200n8"; }; }; + +&sdhci0 { + pinctrl-names = "default","uhs"; + pinctrl-0 = <&mmc1_cfg>; + pinctrl-1 = <&mmc1_uhs_cfg>; + bus-width = <4>; + cd-gpios = <&gpio K3_GPIO(4) GPIO_ACTIVE_HIGH>; + no-mmc; + no-sdio; + disable-wp; + cap-sd-highspeed; + vmmc-supply = <&aldo4>; + vqmmc-supply = <&aldo1>; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi index 846d5e8cc78377..ee53ab2e726733 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -710,4 +710,48 @@ drive-strength = <25>; }; }; + + mmc1_cfg: mmc1-cfg { + mmc1-data-cmd-pins { + pinmux = , /* mmc1 dat3 */ + , /* mmc1 dat2 */ + , /* mmc1 dat1 */ + , /* mmc1 dat0 */ + ; /* mmc1 cmd */ + + bias-pull-up = <0>; + drive-strength = <25>; + power-source = <3300>; + }; + + mmc1-clk-pins { + pinmux = ; /* mmc1 clk */ + + bias-pull-down; + drive-strength = <25>; + power-source = <3300>; + }; + }; + + mmc1_uhs_cfg: mmc1-uhs-cfg { + mmc1-uhs-data-cmd-pins { + pinmux = , /* mmc1 dat3 */ + , /* mmc1 dat2 */ + , /* mmc1 dat1 */ + , /* mmc1 dat0 */ + ; /* mmc1 cmd */ + + bias-pull-up = <0>; + drive-strength = <21>; + power-source = <1800>; + }; + + mmc1-uhs-clk-pins { + pinmux = ; /* mmc1 clk */ + + bias-pull-down; + drive-strength = <21>; + power-source = <1800>; + }; + }; }; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 9a49dd8032bf01..fc46e34465e479 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -1116,6 +1116,19 @@ status = "disabled"; }; + sdhci0: mmc@d4280000 { + compatible = "spacemit,k3-sdhci"; + reg = <0x0 0xd4280000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_SDH_AXI>, + <&syscon_apmu CLK_APMU_SDH0>; + clock-names = "core", "io"; + resets = <&syscon_apmu RESET_APMU_SDH_AXI>, + <&syscon_apmu RESET_APMU_SDH0>; + reset-names = "axi", "sdh"; + interrupts = <99 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + syscon_apmu: system-controller@d4282800 { compatible = "spacemit,k3-syscon-apmu"; reg = <0x0 0xd4282800 0x0 0x400>; From a79f232b7d5420978ec1e10db0e3cb31de6e0bff Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 16 Jun 2026 21:51:31 +0800 Subject: [PATCH 231/521] FROMLIST: ACPI: tables: Add missing #include drivers/acpi/tables.c uses NR_FIX_BTMAPS without including . This isn't a problem for existing archs, but would be when ARCH_HAS_ACPI_TABLE_UPGRADE is enabled for RISC-V. Add the missing include. Signed-off-by: Vivian Wang Link: https://lore.kernel.org/r/20260616-riscv-acpi-table-upgrade-v1-1-45902d2dedf9@iscas.ac.cn Signed-off-by: Han Gao --- drivers/acpi/tables.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 4286e4af10924a..eb93c060426ffd 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "internal.h" #ifdef CONFIG_ACPI_CUSTOM_DSDT From 8c6ab9d96a40f53fc6047fd16650ef132102394c Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 16 Jun 2026 21:51:32 +0800 Subject: [PATCH 232/521] FROMLIST: riscv: acpi: Enable ARCH_HAS_ACPI_TABLE_UPGRADE Implement the various required hooks and enable ARCH_HAS_ACPI_TABLE_UPGRADE to allow use for ACPI_TABLE_UPGRADE, which is useful for debugging ACPI table problems. The implementation is based on arm64's of the same feature due to the similarities of the requirements of the two platforms. Signed-off-by: Vivian Wang Link: https://lore.kernel.org/r/20260616-riscv-acpi-table-upgrade-v1-2-45902d2dedf9@iscas.ac.cn Signed-off-by: Han Gao --- arch/riscv/Kconfig | 1 + arch/riscv/include/asm/acpi.h | 2 ++ arch/riscv/kernel/acpi.c | 5 +++++ arch/riscv/kernel/setup.c | 2 ++ 4 files changed, 10 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a6d24c1d51ef30..278e6157611ae0 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -23,6 +23,7 @@ config RISCV select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM_VMEMMAP select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE + select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_BINFMT_FLAT select ARCH_HAS_CC_CAN_LINK select ARCH_HAS_CURRENT_STACK_POINTER diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h index 26ab37c171bcf6..d59bd06347cc8d 100644 --- a/arch/riscv/include/asm/acpi.h +++ b/arch/riscv/include/asm/acpi.h @@ -92,4 +92,6 @@ void acpi_map_cpus_to_nodes(void); static inline void acpi_map_cpus_to_nodes(void) { } #endif /* CONFIG_ACPI_NUMA */ +#define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE + #endif /*_ASM_ACPI_H*/ diff --git a/arch/riscv/kernel/acpi.c b/arch/riscv/kernel/acpi.c index 068e0b404b6f4a..efd52a5d05b5c0 100644 --- a/arch/riscv/kernel/acpi.c +++ b/arch/riscv/kernel/acpi.c @@ -353,3 +353,8 @@ int acpi_get_cpu_uid(unsigned int cpu, u32 *uid) return 0; } EXPORT_SYMBOL_GPL(acpi_get_cpu_uid); + +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) +{ + memblock_mark_nomap(addr, size); +} diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index 52d1d2b8f338b8..a32344bb220dff 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -321,6 +321,8 @@ void __init setup_arch(char **cmdline_p) efi_init(); paging_init(); + acpi_table_upgrade(); + /* Parse the ACPI tables for possible boot-time configuration */ acpi_boot_table_init(); From b46b07b3f8508e5d18939556e62fd475d95c9b44 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Wed, 4 Feb 2026 21:23:09 +0530 Subject: [PATCH 233/521] FROMLIST: RISC-V: Add common csr_read_num() and csr_write_num() functions In RISC-V, there is no CSR read/write instruction which takes CSR number via register so add common csr_read_num() and csr_write_num() functions which allow accessing certain CSRs by passing CSR number as parameter. These common functions will be first used by the ACPI CPPC driver and RISC-V PMU driver. Also, the RISC-V ACPI FFH specification allows arbitrary CSR number as CPPC register and the RISC-V SBI specification allows arbitrary CSR number as PMU hardware counter. This means ACPI CPPC driver and RISC-V PMU driver no longer need to do sanity checks on CSR number which are now done by the common csr_read_num() and csr_write_num() functions. Signed-off-by: Anup Patel Reviewed-by: Sunil V L Reviewed-by: Andrew Jones Reviewed-by: Atish Patra Reviewed-by: Nutty Liu Link: https://lore.kernel.org/r/20260204155309.763597-2-anup.patel@oss.qualcomm.com Signed-off-by: Han Gao --- arch/riscv/include/asm/csr.h | 3 + arch/riscv/kernel/Makefile | 1 + arch/riscv/kernel/csr.c | 165 +++++++++++++++++++++++++++++++++++ drivers/acpi/riscv/cppc.c | 17 ++-- drivers/perf/riscv_pmu.c | 54 ++---------- 5 files changed, 184 insertions(+), 56 deletions(-) create mode 100644 arch/riscv/kernel/csr.c diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 31b8988f4488da..050da6053800fa 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -606,6 +606,9 @@ : "memory"); \ }) +unsigned long csr_read_num(unsigned long csr_num, int *out_err); +void csr_write_num(unsigned long csr_num, unsigned long val, int *out_err); + #endif /* __ASSEMBLER__ */ #endif /* _ASM_RISCV_CSR_H */ diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index cabb99cadfb6d1..cea8c51decae57 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -51,6 +51,7 @@ obj-y += soc.o obj-$(CONFIG_RISCV_ALTERNATIVE) += alternative.o obj-y += cpu.o obj-y += cpufeature.o +obj-y += csr.o obj-y += entry.o obj-y += irq.o obj-y += process.o diff --git a/arch/riscv/kernel/csr.c b/arch/riscv/kernel/csr.c new file mode 100644 index 00000000000000..e96b129c1a99ed --- /dev/null +++ b/arch/riscv/kernel/csr.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#include +#include +#include +#include + +#define CSR_CUSTOM0_U_RW_BASE 0x800 +#define CSR_CUSTOM0_U_RW_COUNT 0x100 + +#define CSR_CUSTOM1_U_RO_BASE 0xCC0 +#define CSR_CUSTOM1_U_RO_COUNT 0x040 + +#define CSR_CUSTOM2_S_RW_BASE 0x5C0 +#define CSR_CUSTOM2_S_RW_COUNT 0x040 + +#define CSR_CUSTOM3_S_RW_BASE 0x9C0 +#define CSR_CUSTOM3_S_RW_COUNT 0x040 + +#define CSR_CUSTOM4_S_RO_BASE 0xDC0 +#define CSR_CUSTOM4_S_RO_COUNT 0x040 + +#define CSR_CUSTOM5_HS_RW_BASE 0x6C0 +#define CSR_CUSTOM5_HS_RW_COUNT 0x040 + +#define CSR_CUSTOM6_HS_RW_BASE 0xAC0 +#define CSR_CUSTOM6_HS_RW_COUNT 0x040 + +#define CSR_CUSTOM7_HS_RO_BASE 0xEC0 +#define CSR_CUSTOM7_HS_RO_COUNT 0x040 + +#define CSR_CUSTOM8_M_RW_BASE 0x7C0 +#define CSR_CUSTOM8_M_RW_COUNT 0x040 + +#define CSR_CUSTOM9_M_RW_BASE 0xBC0 +#define CSR_CUSTOM9_M_RW_COUNT 0x040 + +#define CSR_CUSTOM10_M_RO_BASE 0xFC0 +#define CSR_CUSTOM10_M_RO_COUNT 0x040 + +unsigned long csr_read_num(unsigned long csr_num, int *out_err) +{ +#define switchcase_csr_read(__csr_num) \ + case (__csr_num): \ + return csr_read(__csr_num) +#define switchcase_csr_read_2(__csr_num) \ + switchcase_csr_read(__csr_num + 0); \ + switchcase_csr_read(__csr_num + 1) +#define switchcase_csr_read_4(__csr_num) \ + switchcase_csr_read_2(__csr_num + 0); \ + switchcase_csr_read_2(__csr_num + 2) +#define switchcase_csr_read_8(__csr_num) \ + switchcase_csr_read_4(__csr_num + 0); \ + switchcase_csr_read_4(__csr_num + 4) +#define switchcase_csr_read_16(__csr_num) \ + switchcase_csr_read_8(__csr_num + 0); \ + switchcase_csr_read_8(__csr_num + 8) +#define switchcase_csr_read_32(__csr_num) \ + switchcase_csr_read_16(__csr_num + 0); \ + switchcase_csr_read_16(__csr_num + 16) +#define switchcase_csr_read_64(__csr_num) \ + switchcase_csr_read_32(__csr_num + 0); \ + switchcase_csr_read_32(__csr_num + 32) +#define switchcase_csr_read_128(__csr_num) \ + switchcase_csr_read_64(__csr_num + 0); \ + switchcase_csr_read_64(__csr_num + 64) +#define switchcase_csr_read_256(__csr_num) \ + switchcase_csr_read_128(__csr_num + 0); \ + switchcase_csr_read_128(__csr_num + 128) + + *out_err = 0; + switch (csr_num) { + switchcase_csr_read_32(CSR_CYCLE); + switchcase_csr_read_32(CSR_CYCLEH); + switchcase_csr_read_256(CSR_CUSTOM0_U_RW_BASE); + switchcase_csr_read_64(CSR_CUSTOM1_U_RO_BASE); + switchcase_csr_read_64(CSR_CUSTOM2_S_RW_BASE); + switchcase_csr_read_64(CSR_CUSTOM3_S_RW_BASE); + switchcase_csr_read_64(CSR_CUSTOM4_S_RO_BASE); + switchcase_csr_read_64(CSR_CUSTOM5_HS_RW_BASE); + switchcase_csr_read_64(CSR_CUSTOM6_HS_RW_BASE); + switchcase_csr_read_64(CSR_CUSTOM7_HS_RO_BASE); +#ifdef CONFIG_RISCV_M_MODE + switchcase_csr_read_64(CSR_CUSTOM8_M_RW_BASE); + switchcase_csr_read_64(CSR_CUSTOM9_M_RW_BASE); + switchcase_csr_read_64(CSR_CUSTOM10_M_RO_BASE); +#endif + default: + *out_err = -EINVAL; + break; + } + + return 0; +#undef switchcase_csr_read_256 +#undef switchcase_csr_read_128 +#undef switchcase_csr_read_64 +#undef switchcase_csr_read_32 +#undef switchcase_csr_read_16 +#undef switchcase_csr_read_8 +#undef switchcase_csr_read_4 +#undef switchcase_csr_read_2 +#undef switchcase_csr_read +} +EXPORT_SYMBOL_GPL(csr_read_num); + +void csr_write_num(unsigned long csr_num, unsigned long val, int *out_err) +{ +#define switchcase_csr_write(__csr_num, __val) \ + case (__csr_num): \ + csr_write(__csr_num, __val); \ + break +#define switchcase_csr_write_2(__csr_num, __val) \ + switchcase_csr_write(__csr_num + 0, __val); \ + switchcase_csr_write(__csr_num + 1, __val) +#define switchcase_csr_write_4(__csr_num, __val) \ + switchcase_csr_write_2(__csr_num + 0, __val); \ + switchcase_csr_write_2(__csr_num + 2, __val) +#define switchcase_csr_write_8(__csr_num, __val) \ + switchcase_csr_write_4(__csr_num + 0, __val); \ + switchcase_csr_write_4(__csr_num + 4, __val) +#define switchcase_csr_write_16(__csr_num, __val) \ + switchcase_csr_write_8(__csr_num + 0, __val); \ + switchcase_csr_write_8(__csr_num + 8, __val) +#define switchcase_csr_write_32(__csr_num, __val) \ + switchcase_csr_write_16(__csr_num + 0, __val); \ + switchcase_csr_write_16(__csr_num + 16, __val) +#define switchcase_csr_write_64(__csr_num, __val) \ + switchcase_csr_write_32(__csr_num + 0, __val); \ + switchcase_csr_write_32(__csr_num + 32, __val) +#define switchcase_csr_write_128(__csr_num, __val) \ + switchcase_csr_write_64(__csr_num + 0, __val); \ + switchcase_csr_write_64(__csr_num + 64, __val) +#define switchcase_csr_write_256(__csr_num, __val) \ + switchcase_csr_write_128(__csr_num + 0, __val); \ + switchcase_csr_write_128(__csr_num + 128, __val) + + *out_err = 0; + switch (csr_num) { + switchcase_csr_write_256(CSR_CUSTOM0_U_RW_BASE, val); + switchcase_csr_write_64(CSR_CUSTOM2_S_RW_BASE, val); + switchcase_csr_write_64(CSR_CUSTOM3_S_RW_BASE, val); + switchcase_csr_write_64(CSR_CUSTOM5_HS_RW_BASE, val); + switchcase_csr_write_64(CSR_CUSTOM6_HS_RW_BASE, val); +#ifdef CONFIG_RISCV_M_MODE + switchcase_csr_write_64(CSR_CUSTOM8_M_RW_BASE, val); + switchcase_csr_write_64(CSR_CUSTOM9_M_RW_BASE, val); +#endif + default: + *out_err = -EINVAL; + break; + } +#undef switchcase_csr_write_256 +#undef switchcase_csr_write_128 +#undef switchcase_csr_write_64 +#undef switchcase_csr_write_32 +#undef switchcase_csr_write_16 +#undef switchcase_csr_write_8 +#undef switchcase_csr_write_4 +#undef switchcase_csr_write_2 +#undef switchcase_csr_write +} +EXPORT_SYMBOL_GPL(csr_write_num); diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c index 42c1a90524707f..fe491937ed2539 100644 --- a/drivers/acpi/riscv/cppc.c +++ b/drivers/acpi/riscv/cppc.c @@ -65,24 +65,19 @@ static void sbi_cppc_write(void *write_data) static void cppc_ffh_csr_read(void *read_data) { struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data; + int err; - switch (data->reg) { - /* Support only TIME CSR for now */ - case CSR_TIME: - data->ret.value = csr_read(CSR_TIME); - data->ret.error = 0; - break; - default: - data->ret.error = -EINVAL; - break; - } + data->ret.value = csr_read_num(data->reg, &err); + data->ret.error = err; } static void cppc_ffh_csr_write(void *write_data) { struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data; + int err; - data->ret.error = -EINVAL; + csr_write_num(data->reg, data->val, &err); + data->ret.error = err; } /* diff --git a/drivers/perf/riscv_pmu.c b/drivers/perf/riscv_pmu.c index 8e3cd0f35336fa..751a8651dc3d8e 100644 --- a/drivers/perf/riscv_pmu.c +++ b/drivers/perf/riscv_pmu.c @@ -16,6 +16,7 @@ #include #include +#include #include static bool riscv_perf_user_access(struct perf_event *event) @@ -88,58 +89,21 @@ void arch_perf_update_userpage(struct perf_event *event, userpg->cap_user_time_short = 1; } -static unsigned long csr_read_num(int csr_num) -{ -#define switchcase_csr_read(__csr_num, __val) {\ - case __csr_num: \ - __val = csr_read(__csr_num); \ - break; } -#define switchcase_csr_read_2(__csr_num, __val) {\ - switchcase_csr_read(__csr_num + 0, __val) \ - switchcase_csr_read(__csr_num + 1, __val)} -#define switchcase_csr_read_4(__csr_num, __val) {\ - switchcase_csr_read_2(__csr_num + 0, __val) \ - switchcase_csr_read_2(__csr_num + 2, __val)} -#define switchcase_csr_read_8(__csr_num, __val) {\ - switchcase_csr_read_4(__csr_num + 0, __val) \ - switchcase_csr_read_4(__csr_num + 4, __val)} -#define switchcase_csr_read_16(__csr_num, __val) {\ - switchcase_csr_read_8(__csr_num + 0, __val) \ - switchcase_csr_read_8(__csr_num + 8, __val)} -#define switchcase_csr_read_32(__csr_num, __val) {\ - switchcase_csr_read_16(__csr_num + 0, __val) \ - switchcase_csr_read_16(__csr_num + 16, __val)} - - unsigned long ret = 0; - - switch (csr_num) { - switchcase_csr_read_32(CSR_CYCLE, ret) - switchcase_csr_read_32(CSR_CYCLEH, ret) - default : - break; - } - - return ret; -#undef switchcase_csr_read_32 -#undef switchcase_csr_read_16 -#undef switchcase_csr_read_8 -#undef switchcase_csr_read_4 -#undef switchcase_csr_read_2 -#undef switchcase_csr_read -} - /* * Read the CSR of a corresponding counter. */ unsigned long riscv_pmu_ctr_read_csr(unsigned long csr) { - if (csr < CSR_CYCLE || csr > CSR_HPMCOUNTER31H || - (csr > CSR_HPMCOUNTER31 && csr < CSR_CYCLEH)) { - pr_err("Invalid performance counter csr %lx\n", csr); - return -EINVAL; + unsigned long val; + int rc; + + val = csr_read_num(csr, &rc); + if (rc) { + pr_err("Failed to read performance counter csr %lx (error %d)\n", csr, rc); + return rc; } - return csr_read_num(csr); + return val; } u64 riscv_pmu_ctr_get_width_mask(struct perf_event *event) From 90a29cbdf34e2c5e09777df5240ae31be4d1fb9d Mon Sep 17 00:00:00 2001 From: Wang Yechao Date: Tue, 9 Jun 2026 19:28:56 +0800 Subject: [PATCH 234/521] FROMLIST: ACPI: RISC-V: Fix false warning in cppc_read_ffh() for same-CPU reads Commit 997c021abc6e ("cpufreq: CPPC: Update FIE arch_freq_scale in ticks for non-PCC regs") changed the CPPC Frequency Invariance Engine to read AMU counters directly from the scheduler tick for non-PCC register spaces (like FFH), instead of deferring to a kthread. This means cppc_read_ffh() is now called with IRQs disabled from the tick handler, triggering the warning. This is the same fix as commit df6e4ab654dc ("arm64: topology: Fix false warning in counters_read_on_cpu() for same-CPU reads"). Fixes: 997c021abc6e ("cpufreq: CPPC: Update FIE arch_freq_scale in ticks for non-PCC regs") Signed-off-by: Wang Yechao Link: https://lore.kernel.org/r/20260609192856874Y8AaPpKL5TnmbgiWI8DI3@zte.com.cn Signed-off-by: Han Gao --- drivers/acpi/riscv/cppc.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c index fe491937ed2539..41c23db0223a20 100644 --- a/drivers/acpi/riscv/cppc.c +++ b/drivers/acpi/riscv/cppc.c @@ -93,16 +93,19 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val) { struct sbi_cppc_data data; - if (WARN_ON_ONCE(irqs_disabled())) - return -EPERM; - if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) { if (!cppc_ext_present) return -EINVAL; data.reg = FFH_CPPC_SBI_REG(reg->address); - smp_call_function_single(cpu, sbi_cppc_read, &data, 1); + if (irqs_disabled()) { + if (WARN_ON_ONCE(cpu != smp_processor_id())) + return -EPERM; + sbi_cppc_read(&data); + } else { + smp_call_function_single(cpu, sbi_cppc_read, &data, 1); + } *val = data.ret.value; @@ -110,7 +113,13 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val) } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) { data.reg = FFH_CPPC_CSR_NUM(reg->address); - smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1); + if (irqs_disabled()) { + if (WARN_ON_ONCE(cpu != smp_processor_id())) + return -EPERM; + cppc_ffh_csr_read(&data); + } else { + smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1); + } *val = data.ret.value; From eb934c6b5b29caed69dcaf32ed5290c21707640b Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Wed, 17 Jun 2026 05:38:24 +0200 Subject: [PATCH 235/521] FROMLIST: riscv: unaligned: stop using kthread for check_vector_unaligned_access() A kthread is used to run check_vector_unaligned_access() to optimize boot time, allowing the kernel to continue booting without waiting for the unaligned vector speed probe to finish. However, this asynchronous approach introduces several complications. First, the kthread may not complete before a user reads vDSO data, resulting in incorrect values. This was previously addressed by commit 5d15d2ad36b0 ("riscv: hwprobe: Fix stale vDSO data for late-initialized keys at boot"), which added complex synchronization between the kthread and vDSO reads. Second, it was discovered that the kthread may not finish before vec_check_unaligned_access_speed_all_cpus() (marked with __init) is freed, triggering a page fault. These issues raise the question of whether the kthread is worth the added complexity. A past boot time regression report was actually unrelated to synchronous probing; it was caused by the probe running serially. Since switching to a parallel probe, no further complaints have been made. Furthermore, the unaligned scalar access speed probe takes the same amount of time, runs synchronously, and has caused no issues. Testing shows no noticeable boot time slowdown when running the vector probe synchronously (0.464474s with kthread vs. 0.457991s without). Remove the kthread usage and run the probe synchronously. This simplifies the boot flow and allows for the revert of commit 5d15d2ad36b0 ("riscv: hwprobe: Fix stale vDSO data for late-initialized keys at boot") Reported-by: Anirudh Srinivasan Closes: https://lore.kernel.org/linux-riscv/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com/ Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions") Cc: stable@vger.kernel.org Signed-off-by: Nam Cao Link: https://lore.kernel.org/r/1c378963f27c5960e8a57c50b8b444d30954cb54.1781666867.git.namcao@linutronix.de Signed-off-by: Han Gao --- arch/riscv/kernel/unaligned_access_speed.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 11c781a4de733a..af437e909f0357 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -313,18 +312,9 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus __free_pages(page, MISALIGNED_BUFFER_ORDER); } -/* Measure unaligned access speed on all CPUs present at boot in parallel. */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) -{ - schedule_on_each_cpu(check_vector_unaligned_access); - riscv_hwprobe_complete_async_probe(); - - return 0; -} #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static void check_vector_unaligned_access(struct work_struct *work __always_unused) { - return 0; } #endif @@ -412,12 +402,7 @@ static int __init check_unaligned_access_all_cpus(void) per_cpu(vector_misaligned_access, cpu) = unaligned_vector_speed_param; } else if (!check_vector_unaligned_access_emulated_all_cpus() && IS_ENABLED(CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS)) { - riscv_hwprobe_register_async_probe(); - if (IS_ERR(kthread_run(vec_check_unaligned_access_speed_all_cpus, - NULL, "vec_check_unaligned_access_speed_all_cpus"))) { - pr_warn("Failed to create vec_unalign_check kthread\n"); - riscv_hwprobe_complete_async_probe(); - } + schedule_on_each_cpu(check_vector_unaligned_access); } /* From 3f4b2e2094e9acba2788d5b1703da94b2d1e6722 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Wed, 17 Jun 2026 05:38:25 +0200 Subject: [PATCH 236/521] FROMLIST: Revert "riscv: hwprobe: Fix stale vDSO data for late-initialized keys at boot" This reverts commit 5d15d2ad36b0 ("riscv: hwprobe: Fix stale vDSO data for late-initialized keys at boot"). The commit ensures synchronization between the unaligned vector access speed probe kthread and vDSO data read. But now that the kthread has been removed, this commit can be reverted. Signed-off-by: Nam Cao Link: https://lore.kernel.org/r/50ca78a649faf53f8941bc94c9cf8268b3644d38.1781666867.git.namcao@linutronix.de Signed-off-by: Han Gao --- arch/riscv/include/asm/hwprobe.h | 7 --- arch/riscv/include/asm/vdso/arch_data.h | 6 --- arch/riscv/kernel/sys_hwprobe.c | 70 +++++-------------------- arch/riscv/kernel/vdso/hwprobe.c | 2 +- 4 files changed, 13 insertions(+), 72 deletions(-) diff --git a/arch/riscv/include/asm/hwprobe.h b/arch/riscv/include/asm/hwprobe.h index 8b9f5e1cf4cb51..9b04377c0f98fa 100644 --- a/arch/riscv/include/asm/hwprobe.h +++ b/arch/riscv/include/asm/hwprobe.h @@ -43,11 +43,4 @@ static inline bool riscv_hwprobe_pair_cmp(struct riscv_hwprobe *pair, return pair->value == other_pair->value; } -#ifdef CONFIG_MMU -void riscv_hwprobe_register_async_probe(void); -void riscv_hwprobe_complete_async_probe(void); -#else -static inline void riscv_hwprobe_register_async_probe(void) {} -static inline void riscv_hwprobe_complete_async_probe(void) {} -#endif #endif diff --git a/arch/riscv/include/asm/vdso/arch_data.h b/arch/riscv/include/asm/vdso/arch_data.h index 88b37af5517512..da57a3786f7a53 100644 --- a/arch/riscv/include/asm/vdso/arch_data.h +++ b/arch/riscv/include/asm/vdso/arch_data.h @@ -12,12 +12,6 @@ struct vdso_arch_data { /* Boolean indicating all CPUs have the same static hwprobe values. */ __u8 homogeneous_cpus; - - /* - * A gate to check and see if the hwprobe data is actually ready, as - * probing is deferred to avoid boot slowdowns. - */ - __u8 ready; }; #endif /* __RISCV_ASM_VDSO_ARCH_DATA_H */ diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c index 1659d31fd288fc..2ce162f5cf7c5d 100644 --- a/arch/riscv/kernel/sys_hwprobe.c +++ b/arch/riscv/kernel/sys_hwprobe.c @@ -5,9 +5,6 @@ * more details. */ #include -#include -#include -#include #include #include #include @@ -504,32 +501,28 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs, return 0; } -#ifdef CONFIG_MMU - -static DECLARE_COMPLETION(boot_probes_done); -static atomic_t pending_boot_probes = ATOMIC_INIT(1); - -void riscv_hwprobe_register_async_probe(void) +static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs, + size_t pair_count, size_t cpusetsize, + unsigned long __user *cpus_user, + unsigned int flags) { - atomic_inc(&pending_boot_probes); -} + if (flags & RISCV_HWPROBE_WHICH_CPUS) + return hwprobe_get_cpus(pairs, pair_count, cpusetsize, + cpus_user, flags); -void riscv_hwprobe_complete_async_probe(void) -{ - if (atomic_dec_and_test(&pending_boot_probes)) - complete(&boot_probes_done); + return hwprobe_get_values(pairs, pair_count, cpusetsize, + cpus_user, flags); } -static int complete_hwprobe_vdso_data(void) +#ifdef CONFIG_MMU + +static int __init init_hwprobe_vdso_data(void) { struct vdso_arch_data *avd = vdso_k_arch_data; u64 id_bitsmash = 0; struct riscv_hwprobe pair; int key; - if (unlikely(!atomic_dec_and_test(&pending_boot_probes))) - wait_for_completion(&boot_probes_done); - /* * Initialize vDSO data with the answers for the "all CPUs" case, to * save a syscall in the common case. @@ -557,52 +550,13 @@ static int complete_hwprobe_vdso_data(void) * vDSO should defer to the kernel for exotic cpu masks. */ avd->homogeneous_cpus = id_bitsmash != 0 && id_bitsmash != -1; - - /* - * Make sure all the VDSO values are visible before we look at them. - * This pairs with the implicit "no speculativly visible accesses" - * barrier in the VDSO hwprobe code. - */ - smp_wmb(); - avd->ready = true; - return 0; -} - -static int __init init_hwprobe_vdso_data(void) -{ - struct vdso_arch_data *avd = vdso_k_arch_data; - - /* - * Prevent the vDSO cached values from being used, as they're not ready - * yet. - */ - avd->ready = false; return 0; } arch_initcall_sync(init_hwprobe_vdso_data); -#else - -static int complete_hwprobe_vdso_data(void) { return 0; } - #endif /* CONFIG_MMU */ -static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs, - size_t pair_count, size_t cpusetsize, - unsigned long __user *cpus_user, - unsigned int flags) -{ - DO_ONCE_SLEEPABLE(complete_hwprobe_vdso_data); - - if (flags & RISCV_HWPROBE_WHICH_CPUS) - return hwprobe_get_cpus(pairs, pair_count, cpusetsize, - cpus_user, flags); - - return hwprobe_get_values(pairs, pair_count, cpusetsize, - cpus_user, flags); -} - SYSCALL_DEFINE5(riscv_hwprobe, struct riscv_hwprobe __user *, pairs, size_t, pair_count, size_t, cpusetsize, unsigned long __user *, cpus, unsigned int, flags) diff --git a/arch/riscv/kernel/vdso/hwprobe.c b/arch/riscv/kernel/vdso/hwprobe.c index 8f45500d0a6e76..2ddeba6c68dda0 100644 --- a/arch/riscv/kernel/vdso/hwprobe.c +++ b/arch/riscv/kernel/vdso/hwprobe.c @@ -27,7 +27,7 @@ static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count, * homogeneous, then this function can handle requests for arbitrary * masks. */ - if (flags != 0 || (!all_cpus && !avd->homogeneous_cpus) || unlikely(!avd->ready)) + if ((flags != 0) || (!all_cpus && !avd->homogeneous_cpus)) return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags); /* This is something we can handle, fill out the pairs. */ From d89acfa60e4f2063fca45973899827c618976def Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Tue, 23 Jun 2026 16:35:51 +0800 Subject: [PATCH 237/521] FROMLIST: riscv: uaccess: Add a fast path for small aligned copies The scalar user copy routine currently enters the word-copy path only for copies of at least 9 * SZREG - 1 bytes. On RV64 this is 71 bytes, so common 32-byte and 64-byte aligned user/kernel copies fall back to byte-by-byte copying. Add a small-copy word path for copies below that threshold when both source and destination are SZREG-aligned. Unaligned and sub-word copies keep using the existing byte-copy path, and larger copies keep using the existing large-copy paths. This is a generic usercopy improvement. One common case is Linux AIO: io_getevents() copies 32-byte struct io_event objects to userspace, and io_submit() copies 64-byte struct iocb objects from userspace. fio libaio randrw three-run averages improved: baseline: read 560k IOPS, write 240k IOPS, sys 68.60% this patch: read 593k IOPS, write 254k IOPS, sys 69.03% This is about +5.8% read IOPS and +5.5% write IOPS. lmbench bw_pipe with small messages was also improved: bw_pipe -m -M 16m -W 1 -N 5 size baseline MB/s this patch MB/s change 32 62.52 74.54 +19.2% 64 122.57 155.31 +26.7% Signed-off-by: Yunhui Cui Link: https://lore.kernel.org/r/20260623083551.86021-1-cuiyunhui@bytedance.com Signed-off-by: Han Gao --- arch/riscv/lib/uaccess.S | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/arch/riscv/lib/uaccess.S b/arch/riscv/lib/uaccess.S index 4efea1b3326c8d..2d78007f10d015 100644 --- a/arch/riscv/lib/uaccess.S +++ b/arch/riscv/lib/uaccess.S @@ -70,12 +70,27 @@ SYM_FUNC_START(fallback_scalar_usercopy_sum_enabled) add t0, a0, a2 /* - * Use byte copy only if too small. - * SZREG holds 4 for RV32 and 8 for RV64 + * For small copies below the large-copy threshold, use word-copy if + * both src and dst are naturally aligned. Unaligned or sub-word copies + * are left to the byte-copy tail. */ li a3, 9*SZREG-1 /* size must >= (word_copy stride + SZREG-1) */ + bgeu a2, a3, .Llarge_copy_user + li a3, SZREG bltu a2, a3, .Lbyte_copy_tail + or a3, a0, a1 + andi a3, a3, SZREG-1 + bnez a3, .Lbyte_copy_tail + andi t1, t0, ~(SZREG-1) +5: + fixup REG_L a5, 0(a1), 10f + addi a1, a1, SZREG + fixup REG_S a5, 0(a0), 10f + addi a0, a0, SZREG + bltu a0, t1, 5b + j .Lbyte_copy_tail +.Llarge_copy_user: /* * Copy first bytes until dst is aligned to word boundary. * a0 - start of dst From 706295715c34799bfccfd6d98928a014472cbf55 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:43:45 +0200 Subject: [PATCH 238/521] FROMLIST: riscv: dts: spacemit: set console baud rate on K3 Pico-ITX board Because the default console's baud rate is not set, defconfig kernels do not have any serial output on this platform. Set the baud rate to 115200, matching what is used by U-Boot etc on this platform. Fixes: 7a6131804986 ("riscv: dts: spacemit: add K3 Pico-ITX board support") Signed-off-by: Aurelien Jarno Reviewed-by: Guodong Xu Link: https://lore.kernel.org/r/20260623204431.498700-2-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index fd2b154f275a35..7f223610449083 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -18,7 +18,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; memory@100000000 { From 5da2700dff0b3f736568b8df1a01fa7483fe1baa Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:43:46 +0200 Subject: [PATCH 239/521] FROMLIST: riscv: dts: spacemit: enable the i2c2 adapter on K3 Pico-ITX board Define I2C2 pinctrl properties, and enable it on the K3 Pico-ITX board. It will be used by the 24c02 eeprom. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260623204431.498700-3-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 6 ++++++ arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index 7f223610449083..20848a5d2f9c71 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -43,6 +43,12 @@ }; }; +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_1_cfg>; + status = "okay"; +}; + &i2c8 { pinctrl-names = "default"; pinctrl-0 = <&i2c8_cfg>; diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi index ee53ab2e726733..5b9045e014345d 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -78,6 +78,17 @@ }; }; + /omit-if-no-ref/ + i2c2_1_cfg: i2c2-1-cfg { + i2c2-1-pins { + pinmux = , /* i2c2 scl */ + ; /* i2c2 sda */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + /omit-if-no-ref/ i2c8_cfg: i2c8-cfg { i2c8-pins { From 6a01f5249392ac35c585fec34a577222c13db9db Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:43:47 +0200 Subject: [PATCH 240/521] FROMLIST: riscv: dts: spacemit: add 24c02 eeprom on K3 Pico-ITX board The K3 Pico-ITX board includes a 24c02 eeprom, that stores the product name, the part name, the MAC address of the network interfaces and the board's serial number. These values are also exposed via an onie,tlv-layout nvmem layout. The eeprom is marked as read-only since its contents are not supposed to be modified. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260623204431.498700-4-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index 20848a5d2f9c71..85dd87a4381e5c 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -47,6 +47,36 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c2_1_cfg>; status = "okay"; + + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + pagesize = <8>; + read-only; + size = <256>; + status = "okay"; + + nvmem-layout { + compatible = "onie,tlv-layout"; + + mac-address { + #nvmem-cell-cells = <1>; + }; + + num-macs { + }; + + part-number { + }; + + product-name { + }; + + serial-number { + }; + + }; + }; }; &i2c8 { From 9c67a9a0df02d46f4442f98fe1ff227927582ea4 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:43:48 +0200 Subject: [PATCH 241/521] FROMLIST: riscv: dts: spacemit: add i2c aliases on K3 Pico-ITX board Add i2c aliases for i2c2 and i2c8 on K3 Pico-ITX board. This is useful to keep a stable number for the /dev entries after loading the i2c-dev module. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260623204431.498700-5-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index 85dd87a4381e5c..a07db9853ad89d 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -14,6 +14,8 @@ aliases { ethernet0 = ð0; + i2c2 = &i2c2; + i2c8 = &i2c8; serial0 = &uart0; }; From 165b8ec82f65f6d732a019ce041dd24e79160d15 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:43:49 +0200 Subject: [PATCH 242/521] FROMLIST: riscv: dts: spacemit: improve RTL8211F PHY configuration on K3 Pico-ITX board Vendor kernel enabled ALDPS (Advanced Link Down Power Saving) on the RTL8211F PHY to save power when link down. Vendor kernel also disabled the 125MHz clkout clock signal, and indeed the schematics confirms that it only goes to a test point (TP14), so let's do the same. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260623204431.498700-6-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index a07db9853ad89d..8b2a120b02d15f 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -234,6 +234,8 @@ reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; reset-deassert-us = <10000>; + realtek,aldps-enable; + realtek,clkout-disable; }; }; }; From 90bec132f17114d97115381c27e1173edd505775 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Thu, 25 Jun 2026 06:04:34 +0800 Subject: [PATCH 243/521] FROMLIST: clocksource/drivers/timer-sun4i: Advertise a real minimum delta sun4i_clkevt_next_event() compensates for the timer stop/start synchronization delay by programming evt - TIMER_SYNC_TICKS into the hardware interval register. The clockevent device currently advertises TIMER_SYNC_TICKS as min_delta_ticks, so the clockevents core is allowed to call set_next_event() with evt == TIMER_SYNC_TICKS. That programs a zero-tick interval. With oneshot/highres/nohz timer operation this can leave the next event stuck, which was observed as a boot hang on Allwinner D1 after the clockevents core started reusing forced minimum-delta events. Advertise one extra tick instead, so the smallest event accepted by the core still programs at least one hardware tick after the synchronization compensation. Fixes: 12e1480bcb49 ("clocksource: sun4i: Report the minimum tick that we can program") Cc: stable@vger.kernel.org Reported-by: Indrek Kruusa Closes: https://lore.kernel.org/linux-riscv/CA+fTLhgLmTY+exGujKf8OYYQvcEW5X5NJ_5sLq2AYL6zER2c0A@mail.gmail.com/ Assisted-by: Codex:gpt-5.5 Signed-off-by: Felix Yan Acked-by: Jernej Skrabec Tested-by: Indrek Kruusa Link: https://lore.kernel.org/linux-riscv/CA+fTLhgLmTY+exGujKf8OYYQvcEW5X5NJ_5sLq2AYL6zER2c0A@mail.gmail.com/ Link: https://lore.kernel.org/r/20260624220434.4183732-1-felixonmars@archlinux.org Signed-off-by: Han Gao --- drivers/clocksource/timer-sun4i.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-sun4i.c b/drivers/clocksource/timer-sun4i.c index 7bdcc60ad43cb5..c2d04ab7cf2d38 100644 --- a/drivers/clocksource/timer-sun4i.c +++ b/drivers/clocksource/timer-sun4i.c @@ -208,7 +208,7 @@ static int __init sun4i_timer_init(struct device_node *node) sun4i_timer_clear_interrupt(timer_of_base(&to)); clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), - TIMER_SYNC_TICKS, 0xffffffff); + TIMER_SYNC_TICKS + 1, 0xffffffff); /* Enable timer0 interrupt */ val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG); From 812dc1a98b761de3bbda509b0b4ed52990632013 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Sat, 20 Jun 2026 01:07:48 -0400 Subject: [PATCH 244/521] FROMLIST: riscv: dts: spacemit: Use symbolic PDMA request numbers on K1 The PDMA request numbers (DRQ) are fixed values specific to the SoC from a hardware perspective. The detailed definition can be found in K1 User Manual [1], Chapter 9.4.3 DMA Connectivity & Assignments. Add a DTS header file to define the symbolic names for the DRQs of non-secure DMA peripherals. Convert the K1 SPI3 node to these macros. Link: https://www.spacemit.com/community/document/info?lang=en&nodepath=hardware/key_stone/k1/k1_docs/k1_usermanual/9.Top_System.md [1] Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20260620-b4-k1-pdma-req-macros-v4-1-3cf77d0bd0d6@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k1-pdma.h | 56 ++++++++++++++++++++++++++ arch/riscv/boot/dts/spacemit/k1.dtsi | 4 +- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/boot/dts/spacemit/k1-pdma.h diff --git a/arch/riscv/boot/dts/spacemit/k1-pdma.h b/arch/riscv/boot/dts/spacemit/k1-pdma.h new file mode 100644 index 00000000000000..7e5ad3d7111d46 --- /dev/null +++ b/arch/riscv/boot/dts/spacemit/k1-pdma.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * DMA request number (DRQ) definitions for non-secure peripherals of + * the SpacemiT K1 PDMA. + * + * Copyright (c) 2026 Guodong Xu + */ + +#ifndef _DTS_SPACEMIT_K1_PDMA_H +#define _DTS_SPACEMIT_K1_PDMA_H + +#define K1_PDMA_UART0_TX 3 +#define K1_PDMA_UART0_RX 4 +#define K1_PDMA_UART2_TX 5 +#define K1_PDMA_UART2_RX 6 +#define K1_PDMA_UART3_TX 7 +#define K1_PDMA_UART3_RX 8 +#define K1_PDMA_UART4_TX 9 +#define K1_PDMA_UART4_RX 10 +#define K1_PDMA_I2C0_TX 11 +#define K1_PDMA_I2C0_RX 12 +#define K1_PDMA_I2C1_TX 13 +#define K1_PDMA_I2C1_RX 14 +#define K1_PDMA_I2C2_TX 15 +#define K1_PDMA_I2C2_RX 16 +#define K1_PDMA_I2C4_TX 17 +#define K1_PDMA_I2C4_RX 18 +#define K1_PDMA_SPI3_TX 19 +#define K1_PDMA_SPI3_RX 20 +#define K1_PDMA_I2S0_TX 21 +#define K1_PDMA_I2S0_RX 22 +#define K1_PDMA_I2S1_TX 23 +#define K1_PDMA_I2S1_RX 24 +#define K1_PDMA_UART5_TX 25 +#define K1_PDMA_UART5_RX 26 +#define K1_PDMA_UART6_TX 27 +#define K1_PDMA_UART6_RX 28 +#define K1_PDMA_UART7_TX 29 +#define K1_PDMA_UART7_RX 30 +#define K1_PDMA_UART8_TX 31 +#define K1_PDMA_UART8_RX 32 +#define K1_PDMA_UART9_TX 33 +#define K1_PDMA_UART9_RX 34 +#define K1_PDMA_I2C5_TX 35 +#define K1_PDMA_I2C5_RX 36 +#define K1_PDMA_I2C6_TX 37 +#define K1_PDMA_I2C6_RX 38 +#define K1_PDMA_I2C7_TX 39 +#define K1_PDMA_I2C7_RX 40 +#define K1_PDMA_I2C8_TX 41 +#define K1_PDMA_I2C8_RX 42 +#define K1_PDMA_CAN0_RX 43 +#define K1_PDMA_QSPI_RX 44 +#define K1_PDMA_QSPI_TX 45 + +#endif /* _DTS_SPACEMIT_K1_PDMA_H */ diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index 4a359190f63350..3fabfd34114b23 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -6,6 +6,8 @@ #include #include +#include "k1-pdma.h" + /dts-v1/; / { #address-cells = <2>; @@ -1102,7 +1104,7 @@ clock-names = "core", "bus"; resets = <&syscon_apbc RESET_SSP3>; interrupts = <55>; - dmas = <&pdma 20>, <&pdma 19>; + dmas = <&pdma K1_PDMA_SPI3_RX>, <&pdma K1_PDMA_SPI3_TX>; dma-names = "rx", "tx"; status = "disabled"; }; From 683ed424e0772ecb99d5ee1facfdb9a6b65232ad Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Mon, 29 Jun 2026 17:25:45 +0800 Subject: [PATCH 245/521] FROMLIST: riscv: mm: Fix concurrency in mark_new_valid_map() Turns out, the concurrency concerns [1] were justified - BOSC reported a spurious fault in KFENCE that still triggers despite previous fixes, which KFENCE reports as a false-positive. Fix the concurrency problems in mark_new_valid_map(): - Add smp_wmb() before filling the bitmap, to make sure page table writes are "received". - Use WRITE_ONCE() to fill the bitmap. This fixes the KFENCE false positives in internal testing. Also update comments in the assembly exception handler code to match. Fixes: 26c171fc4853 ("riscv: mm: Use the bitmap API for new_valid_map_cpus") Reported-by: Yaxing Guo Suggested-by: David Hildenbrand (Arm) Link: https://lore.kernel.org/linux-riscv/da19ffcf-8042-4f96-9c2d-649468dc6a0a@kernel.org/ # [1] Signed-off-by: Vivian Wang Link: https://lore.kernel.org/r/20260629-riscv-mm-new-valid-map-ordering-v1-1-60d8c10c6292@iscas.ac.cn Signed-off-by: Han Gao --- arch/riscv/include/asm/cacheflush.h | 15 ++++++++++++++- arch/riscv/kernel/entry.S | 11 ++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index c2b0a2928f0674..c6297a1d26459c 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -6,7 +6,9 @@ #ifndef _ASM_RISCV_CACHEFLUSH_H #define _ASM_RISCV_CACHEFLUSH_H +#include #include +#include static inline void local_flush_icache_all(void) { @@ -46,12 +48,23 @@ extern DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); extern char _end[]; static inline void mark_new_valid_map(void) { + /* + * Orders any previous page table writes before setting bits in + * new_valid_map_cpus. Pairs with the sfence.vma in + * new_valid_map_cpus_check. + */ + smp_wmb(); + /* * We don't care if concurrently a cpu resets this value since * the only place this can happen is in handle_exception() where * an sfence.vma is emitted. + * + * Not memset() or bitmap_fill() to avoid any possible compiler + * shenanigans. */ - bitmap_fill(new_valid_map_cpus, NR_CPUS); + for (size_t i = 0; i < ARRAY_SIZE(new_valid_map_cpus); i++) + WRITE_ONCE(new_valid_map_cpus[i], -1UL); } #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 3ae498779adff3..31037385d984c2 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -76,6 +76,8 @@ amoxor.d a0, a1, (a0) /* + * Pairs with the smp_wmb() in mark_new_valid_map() + * * A sfence.vma is required here. Even if we had Svvptc, there's no * guarantee that after returning we wouldn't just fault again. */ @@ -141,13 +143,8 @@ SYM_CODE_START(handle_exception) /* * The RISC-V kernel does not flush TLBs on all CPUS after each new * vmalloc mapping or kfence_unprotect(), which may result in - * exceptions: - * - * - if the uarch caches invalid entries, the new mapping would not be - * observed by the page table walker and an invalidation is needed. - * - if the uarch does not cache invalid entries, a reordered access - * could "miss" the new mapping and traps: in that case, we only need - * to retry the access, no sfence.vma is required. + * exceptions. In that case, we need to sfence.vma to "receive" the new + * mappings and retry, whether or not we have Svvptc. */ new_valid_map_cpus_check #endif From 03b0e947b595d131dfd78f5fddafbd2a6cead3df Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 30 Jun 2026 12:52:45 +0000 Subject: [PATCH 246/521] FROMLIST: clk: spacemit: k3: fix parent clock of UFS aclk According to SpacemiT updated clock docs, the previous UFS aclk parent clock was wrong, the correct one is illustrated below, so fix it. --> pll1_d5_491p52 --\ --> pll1_d6_409p6 --| --> pll2_d6 --|--> div --> gate --> ufs_aclk --> pll2_d5 --/ Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260630-06-clk-ufs-support-v1-1-cf7521d1d0fe@kernel.org Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 03de0414496377..0702b6ecdbd255 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -926,11 +926,10 @@ CCU_MUX_DIV_GATE_FC_DEFINE(dpu_aclk, dpu_aclk_parents, APMU_LCD_CLK_RES_CTRL5, 1 20, 3, BIT(16), 0); static const struct clk_parent_data ufs_aclk_parents[] = { - CCU_PARENT_HW(pll1_d6_409p6), CCU_PARENT_HW(pll1_d5_491p52), - CCU_PARENT_HW(pll1_d4_614p4), - CCU_PARENT_HW(pll1_d8_307p2), - CCU_PARENT_HW(pll2_d4), + CCU_PARENT_HW(pll1_d6_409p6), + CCU_PARENT_HW(pll2_d6), + CCU_PARENT_HW(pll2_d5), }; CCU_MUX_DIV_GATE_FC_DEFINE(ufs_aclk, ufs_aclk_parents, APMU_UFS_CLK_RES_CTRL, 5, 3, BIT(8), 2, 3, BIT(1), 0); From 1e249d87c6cae1f54654d4de36a5fa4dfffbb3f4 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 30 Jun 2026 12:52:46 +0000 Subject: [PATCH 247/521] FROMLIST: dt-bindings: soc: spacemit: k3: Add clock ID for UFS refclk Add clock ID for the UFS refclk clock in SpacemiT K3 SoC. Signed-off-by: Yixun Lan Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260630-06-clk-ufs-support-v1-2-cf7521d1d0fe@kernel.org Signed-off-by: Han Gao --- include/dt-bindings/clock/spacemit,k3-clocks.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/spacemit,k3-clocks.h b/include/dt-bindings/clock/spacemit,k3-clocks.h index dfae52547cda49..69978ab0b29f86 100644 --- a/include/dt-bindings/clock/spacemit,k3-clocks.h +++ b/include/dt-bindings/clock/spacemit,k3-clocks.h @@ -385,6 +385,7 @@ #define CLK_APMU_PCIE_PORTC_DBI 91 #define CLK_APMU_PCIE_PORTD_DBI 92 #define CLK_APMU_PCIE_PORTE_DBI 93 +#define CLK_APMU_UFS_REFCLK 94 /* DCIU clocks */ #define CLK_DCIU_HDMA 0 From 138f994cfe0070772c75c22c981ee37e0896b324 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 30 Jun 2026 12:52:47 +0000 Subject: [PATCH 248/521] FROMLIST: clk: spacemit: k3: Add UFS refclk clock Add the UFS reference clock for SpacemiT K3 SoC. Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260630-06-clk-ufs-support-v1-3-cf7521d1d0fe@kernel.org Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 0702b6ecdbd255..092446590e4169 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -934,6 +934,8 @@ static const struct clk_parent_data ufs_aclk_parents[] = { CCU_MUX_DIV_GATE_FC_DEFINE(ufs_aclk, ufs_aclk_parents, APMU_UFS_CLK_RES_CTRL, 5, 3, BIT(8), 2, 3, BIT(1), 0); +CCU_FACTOR_DEFINE(ufs_refclk, CCU_PARENT_HW(pll1_d64_38p4), 2, 1); + static const struct clk_parent_data edp0_pclk_parents[] = { CCU_PARENT_HW(lcd_pxclk), CCU_PARENT_NAME(external_clk), @@ -1391,6 +1393,7 @@ static struct clk_hw *k3_ccu_apmu_hws[] = { [CLK_APMU_DSI4LN2_DPU_ACLK] = &dsi4ln2_dpu_aclk.common.hw, [CLK_APMU_DPU_ACLK] = &dpu_aclk.common.hw, [CLK_APMU_UFS_ACLK] = &ufs_aclk.common.hw, + [CLK_APMU_UFS_REFCLK] = &ufs_refclk.common.hw, [CLK_APMU_EDP0_PXCLK] = &edp0_pxclk.common.hw, [CLK_APMU_EDP1_PXCLK] = &edp1_pxclk.common.hw, [CLK_APMU_PCIE_PORTA_MSTE] = &pciea_mstr_clk.common.hw, From f5398957120b23d9a8526bf25983dbe019f71acf Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 30 Jun 2026 14:36:24 +0800 Subject: [PATCH 249/521] FROMLIST: riscv: Avoid stack access when CSR_SCRATCH is incorrect The kernel exception handler unconditionally relies on the value in CSR_SCRATCH, whether it is 0 (meaning kernel mode) or the current pointer (meaning user mode). Therefore, any exception that happens while CSR_SCRATCH does not reflect the current privilege mode would be irrecoverable. When VMAP_STACK=y, spurious-seeming page faults can happen while accessing the stack if the processor has cached/still-in-pipeline non-valid PTEs. Therefore, it is unsafe to access the stack on entry before setting CSR_SCRATCH to 0, or on exit to user after setting CSR_SCRATCH to current. Therefore, instead of using the stack, make some scratch space in task_info for these fragile regions of code, and access them using offsets from tp. The scratch space for mark_new_valid_map is not reused because the page fault *can* nest in places where the scratch space is needed. Fixes: 503638e0babf ("riscv: Stop emitting preventive sfence.vma for new vmalloc mappings") Reported-by: Yaxing Guo Link: https://lore.kernel.org/linux-riscv/20260601084524.34584-1-guoyaxing@bosc.ac.cn/ Signed-off-by: Vivian Wang Link: https://lore.kernel.org/r/20260630-riscv-no-stack-when-wrong-scratch-v1-1-0f9b9cb5cf67@iscas.ac.cn Signed-off-by: Han Gao --- arch/riscv/include/asm/csr.h | 1 + arch/riscv/include/asm/thread_info.h | 5 +++ arch/riscv/kernel/asm-offsets.c | 2 + arch/riscv/kernel/entry.S | 65 +++++++++++++++++++++------- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 050da6053800fa..0f69f389c96d37 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -16,6 +16,7 @@ #define SR_MPIE _AC(0x00000080, UL) /* Previous Machine IE */ #define SR_SPP _AC(0x00000100, UL) /* Previously Supervisor */ #define SR_MPP _AC(0x00001800, UL) /* Previously Machine */ +#define SR_MPP_SHIFT 11 #define SR_SUM _AC(0x00040000, UL) /* Supervisor User Memory Access */ /* zicfilp landing pad status bit */ diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index 55019fdfa9ecaa..afa9c61d74c603 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -73,6 +73,11 @@ struct thread_info { */ unsigned long a0, a1, a2; #endif + /* + * Used in ret_from_exception() and handle_exception() as scratch space + * when CSR_SCRATCH does not reflect the current privilege mode. + */ + unsigned long tp, t0; #ifdef CONFIG_RISCV_USER_CFI struct cfi_state user_cfi_state; #endif diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index af827448a609e6..2d617234bcdb4c 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -49,6 +49,8 @@ void asm_offsets(void) OFFSET(TASK_TI_A1, task_struct, thread_info.a1); OFFSET(TASK_TI_A2, task_struct, thread_info.a2); #endif + OFFSET(TASK_TI_TP, task_struct, thread_info.tp); + OFFSET(TASK_TI_T0, task_struct, thread_info.t0); OFFSET(TASK_TI_CPU_NUM, task_struct, thread_info.cpu); #ifdef CONFIG_RISCV_USER_CFI diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 31037385d984c2..da4c9d4e3d01f0 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -128,6 +128,8 @@ .endm SYM_CODE_START(handle_exception) + /* Caution: CSR_SCARTCH does not reflect privilege mode from now on. */ + /* * If coming from userspace, preserve the user thread pointer and load * the kernel thread pointer. If we came from the kernel, the scratch @@ -160,6 +162,19 @@ SYM_CODE_START(handle_exception) #endif .Lsave_context: + REG_S t0, TASK_TI_T0(tp) + + /* + * Grab the saved TP, and also set the scratch register back to 0, so + * that if a recursive exception occurs, the exception vector knows it + * came from the kernel. + * + * Caution: CSR_SCARTCH does not reflect privilege mode before this. + */ + csrrw t0, CSR_SCRATCH, zero + REG_S t0, TASK_TI_TP(tp) + REG_L t0, TASK_TI_T0(tp) + REG_S sp, TASK_TI_USER_SP(tp) REG_L sp, TASK_TI_KERNEL_SP(tp) addi sp, sp, -(PT_SIZE_ON_STACK) @@ -187,7 +202,7 @@ SYM_CODE_START(handle_exception) csrr s2, CSR_EPC csrr s3, CSR_TVAL csrr s4, CSR_CAUSE - csrr s5, CSR_SCRATCH + REG_L s5, TASK_TI_TP(tp) REG_S s0, PT_SP(sp) REG_S s1, PT_STATUS(sp) REG_S s2, PT_EPC(sp) @@ -195,12 +210,6 @@ SYM_CODE_START(handle_exception) REG_S s4, PT_CAUSE(sp) REG_S s5, PT_TP(sp) - /* - * Set the scratch register to 0, so that if a recursive exception - * occurs, the exception vector knows it came from the kernel - */ - csrw CSR_SCRATCH, x0 - /* Load the global pointer */ load_global_pointer @@ -268,11 +277,6 @@ SYM_CODE_START_NOALIGN(ret_from_exception) /* Save the kernel shadow call stack pointer */ scs_save_current - /* - * Save TP into the scratch register , so we can find the kernel data - * structures again. - */ - csrw CSR_SCRATCH, tp 1: #ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE move a0, sp @@ -305,11 +309,42 @@ SYM_CODE_START_NOALIGN(ret_from_exception) REG_L x1, PT_RA(sp) REG_L x3, PT_GP(sp) - REG_L x4, PT_TP(sp) - REG_L x5, PT_T0(sp) restore_from_x6_to_x31 - REG_L x2, PT_SP(sp) + /* + * Stash t0 and tp in thread_info since we can't access the stack after + * switching CSR_SCRATCH. + */ + REG_L t0, PT_T0(sp) + REG_S t0, TASK_TI_T0(tp) + REG_L t0, PT_TP(sp) + REG_S t0, TASK_TI_TP(tp) + + REG_L sp, PT_SP(sp) + + /* If returning to user mode, set CSR_SCRATCH to tp, else 0 */ + csrr t0, CSR_STATUS +#if defined(CONFIG_RISCV_M_MODE) + /* the MPP value is too large to be used as an immediate arg for andi */ + srli t0, t0, SR_MPP_SHIFT + andi t0, t0, SR_MPP >> SR_MPP_SHIFT + /* t0 = (mstatus & SR_MPP) >> SR_MPP_SHIFT */ +#else + andi t0, t0, SR_SPP + /* t0 = sstatus & SR_SPP */ +#endif + seqz t0, t0 + /* t0 = !(status & SR_PP) */ + neg t0, t0 + /* t0 = (status & SR_PP) ? 0 : -1 */ + and t0, t0, tp + /* t0 = (status & SR_PP) ? 0 : tp */ + csrw CSR_SCRATCH, t0 + + /* Caution: CSR_SCARTCH does not reflect privilege mode from now on. */ + + REG_L t0, TASK_TI_T0(tp) + REG_L tp, TASK_TI_TP(tp) #ifdef CONFIG_RISCV_M_MODE mret From cb548dcec7d507bc600c566c816acbf3685335c6 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 30 Jun 2026 08:53:19 +0000 Subject: [PATCH 250/521] FROMLIST: clk: spacemit: k3: set hdma clock as critical HDMA clock is responsible for the internal TCM access path of X100 RISC-V core, so set the clock flag as critical to prevent it from being shut off, otherwise the Linux system will hang. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Reported-by: Han Gao Signed-off-by: Yixun Lan Tested-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260630-06-clk-hdma-critial-v1-1-443c0ac88c5f@kernel.org Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 092446590e4169..aca61ea760194a 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -1027,7 +1027,7 @@ CCU_MUX_DIV_GATE_DEFINE(isim_vclk_out3, isim_vclk_parents, APMU_SNR_ISIM_VCLK_CT /* APMU clocks end */ /* DCIU clocks start */ -CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), 0); +CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), CLK_IS_CRITICAL); CCU_GATE_DEFINE(dma350_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_SDMA_CLK_EN, BIT(0), 0); CCU_GATE_DEFINE(c2_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C2_TCM_PIPE_CLK, BIT(0), 0); CCU_GATE_DEFINE(c3_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C3_TCM_PIPE_CLK, BIT(0), 0); From 410adff5259b87c006aa24bfaed592b1a974c161 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 30 Jun 2026 08:53:20 +0000 Subject: [PATCH 251/521] FROMLIST: clk: spacemit: k3: set rcpu clock as critical The SpacemiT K3 SoC integrates two RT24 RISC-V little cores (os0_rcpu and os1_rcpu), each running an independent ESOS instance responsible for system power management and real-time task. The RCPU clock is required in order to keep these cores functional, so set its clock as critical to prevent it from being shut off. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Reported-by: Han Gao Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260630-06-clk-hdma-critial-v1-2-443c0ac88c5f@kernel.org Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index aca61ea760194a..5d0e31f07d6d2e 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -866,7 +866,7 @@ static const struct clk_parent_data rcpu_clk_parents[] = { CCU_PARENT_HW(pll1_d6_409p6), }; CCU_MUX_DIV_GATE_FC_DEFINE(rcpu_clk, rcpu_clk_parents, APMU_RCPU_CLK_RES_CTRL, - 4, 3, BIT(15), 7, 3, BIT(12), 0); + 4, 3, BIT(15), 7, 3, BIT(12), CLK_IS_CRITICAL); static const struct clk_parent_data dsi4ln2_dsi_esc_parents[] = { CCU_PARENT_HW(pll1_d48_51p2_ap), From 951f370cbedddb61fdc8f32f4b9e9caadfa8866a Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 2 Jul 2026 02:31:35 +0000 Subject: [PATCH 252/521] FROMLIST: scsi: ufs: spacemit: dt-bindings: Add UFS controller for K3 SoC Document the compatible for UFS (Universal Flash Storage) Host Controller which found in SpacemiT K3 SoC chip. Its features are listed below: - Compliant with MIPI UniPro v1.61 specification. - Compliant with M-PHY v3.0 specification. - Compliant with UFS HCI v2.1 specification. - Supports up to 2 Tx and 2 Rx lanes, up to HS-GEAR3 5.8 Gbps per lane. - Supports standard low-power hibernate to reduce power consumption. Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260702-08-k3-ufs-support-v1-1-1a64a3ab128f@kernel.org Signed-off-by: Han Gao --- .../bindings/ufs/spacemit,k3-ufshc.yaml | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/ufs/spacemit,k3-ufshc.yaml diff --git a/Documentation/devicetree/bindings/ufs/spacemit,k3-ufshc.yaml b/Documentation/devicetree/bindings/ufs/spacemit,k3-ufshc.yaml new file mode 100644 index 00000000000000..e6cb6fb2496c85 --- /dev/null +++ b/Documentation/devicetree/bindings/ufs/spacemit,k3-ufshc.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ufs/spacemit,k3-ufshc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K3 SoC UFS Host Controller + +maintainers: + - Yixun Lan + +properties: + compatible: + const: spacemit,k3-ufshc + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: aclk + - const: ref_clk + + resets: + maxItems: 1 + +required: + - reg + - clocks + - clock-names + - resets + +allOf: + - $ref: ufs-common.yaml + +unevaluatedProperties: false + +examples: + - | + #include + #include + ufs@c0e00000 { + compatible = "spacemit,k3-ufshc"; + reg = <0xc0e00000 0x40000>; + clocks = <&syscon_apmu CLK_APMU_UFS_ACLK>, + <&syscon_apmu CLK_APMU_UFS_REFCLK>; + clock-names = "aclk", "ref_clk"; + resets = <&syscon_apmu RESET_APMU_UFS_ACLK>; + freq-table-hz = <491520000 491520000 19200000 19200000>; + lanes-per-direction = <2>; + }; From 12e2d8fd9aea0f04c255057bb555955f17b0551d Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 2 Jul 2026 02:31:36 +0000 Subject: [PATCH 253/521] FROMLIST: scsi: ufs: spacemit: k3: Add UFS Host Controller driver SpacemiT K3 SoC consist of UFS (Universal Flash Storage) Host Controller which has features compatible with JEDEC UFS 2.2, MIPI UniPro v1.61 and M-PHY v3.0 standard. Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260702-08-k3-ufs-support-v1-2-1a64a3ab128f@kernel.org Signed-off-by: Han Gao --- drivers/ufs/host/Kconfig | 12 + drivers/ufs/host/Makefile | 1 + drivers/ufs/host/ufs-spacemit.c | 931 ++++++++++++++++++++++++++++++++ drivers/ufs/host/ufs-spacemit.h | 90 +++ 4 files changed, 1034 insertions(+) create mode 100644 drivers/ufs/host/ufs-spacemit.c create mode 100644 drivers/ufs/host/ufs-spacemit.h diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig index 964ae70e7390d0..9b57772faf5f83 100644 --- a/drivers/ufs/host/Kconfig +++ b/drivers/ufs/host/Kconfig @@ -132,6 +132,18 @@ config SCSI_UFS_VARIABLE_SG_ENTRY_SIZE bool default y if SCSI_UFS_EXYNOS && SCSI_UFS_CRYPTO +config SCSI_UFS_SPACEMIT + tristate "SpacemiT UFS controller driver" + depends on SCSI_UFSHCD_PLATFORM && ARCH_SPACEMIT + help + This selects the SpacemiT specific additions to UFSHCD platform driver. + UFS host on SpacemiT needs some vendor specific configuration before + accessing the hardware which includes PHY configuration and vendor + specific registers. + + Select this if you have UFS controller on SpacemiT chipset. + If unsure, say N. + config SCSI_UFS_SPRD tristate "Unisoc specific hooks to UFS controller platform driver" depends on SCSI_UFSHCD_PLATFORM && (ARCH_SPRD || COMPILE_TEST) diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile index 65d8bb23ab7b3b..151bf43dfba774 100644 --- a/drivers/ufs/host/Makefile +++ b/drivers/ufs/host/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o obj-$(CONFIG_SCSI_UFS_MEDIATEK) += ufs-mediatek.o obj-$(CONFIG_SCSI_UFS_RENESAS) += ufs-renesas.o obj-$(CONFIG_SCSI_UFS_ROCKCHIP) += ufs-rockchip.o +obj-$(CONFIG_SCSI_UFS_SPACEMIT) += ufs-spacemit.o obj-$(CONFIG_SCSI_UFS_SPRD) += ufs-sprd.o obj-$(CONFIG_SCSI_UFS_TI_J721E) += ti-j721e-ufs.o obj-$(CONFIG_SCSI_UFS_AMD_VERSAL2) += ufs-amd-versal2.o ufshcd-dwc.o diff --git a/drivers/ufs/host/ufs-spacemit.c b/drivers/ufs/host/ufs-spacemit.c new file mode 100644 index 00000000000000..be475f5566a839 --- /dev/null +++ b/drivers/ufs/host/ufs-spacemit.c @@ -0,0 +1,931 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 SpacemiT (Hangzhou) Technology Co. Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ufshcd-pltfrm.h" +#include "ufs-spacemit.h" + +/* PA Layer Gettable and settable M-PHY Specific Attributes */ +#define PA_TXHSG1SYNCLENGTH 0x1552 +#define PA_TXHSG1PREPARELENGTH 0x1553 +#define PA_TXHSG2SYNCLENGTH 0x1554 +#define PA_TXHSG2PREPARELENGTH 0x1555 +#define PA_TXHSG3SYNCLENGTH 0x1556 +#define PA_TXHSG3PREPARELENGTH 0x1557 +#define PA_TXMK2EXTENSION 0x155A +#define PA_PEERSCRAMBLING 0x155B +#define PA_TXSKIP 0x155C +#define PA_TXSKIPPERIOD 0x155D +#define PA_PEER_TX_LCC_ENABLE 0x155F + +#define PA_SCRAMBLING 0x1585 +#define PA_MK2EXTENSIONGUARDBAND 0x15AB + +/*special TX/RX Configuration Attributes*/ +#define RX_LS_PRE_LEN_CAP 0x008D +#define RX_LANE_HB8_BKDOOR_ATTR 0x00F4 +#define RX_PWRM_CLOSURE_LEN_CAP 0x008E +#define RX_MIN_STALL_CAP 0x0088 +#define RX_LANE_SOF_BKDOOR_ATT 0x00F2 +#define RX_GARBAGE_COUNT_OFFSET 0x00F2 + +/*special analog reg*/ +#define ANA_EQ_CTRL_REG_ATTR 0x00CD +#define ANA_HSGEAR_CTRL_ATTR 0x00C1 + +/* + * Keep UFS ACLK at a lower parent rate (409.6MHz) for stable init. + * This mirrors the "ufs-low-aclk-freq" change from the other environment. + */ +#define UFS_ACLK_LOW_FREQ_HZ 409600000UL + +/* PHY register magic values */ +#define MPHY_PU_ALL 0x87f +#define MPHY_PU_WITH_HB8_RESET 0xb7f +#define MPHY_DEVICE_RESET_DEASSERT 0x101 +#define MPHY_DEVICE_RESET_ASSERT 0x001 +#define MPHY_PLL_LOCK_BIT BIT(31) +#define MPHY_PLL_LOCK_TIMEOUT_US 10000 + +/* FSM states */ +#define FSM_STATE_HIBERN8 0x1 +#define FSM_STATE_ACTIVE 0x3 +#define FSM_STATE_LS_BURST 0x5 + +struct ufs_reg_snapshot { + u32 reg_utrlba; + u32 reg_utrlbau; + u32 reg_utrmlba; + u32 reg_utrmlbau; + + u32 reg_sys1clk; + u32 reg_tx_symbol_clk; + u32 reg_retry_timer; + u32 reg_pa_link; + u32 reg_cfg1; +}; + +#define VENDOR_DUMP_BUF_SIZE 2048 +#define VENDOR_MAX_OFFSET 0xE0 + +static void ufs_spacemit_dump_host_regs(struct ufs_hba *hba) +{ + int i, offset; + size_t len = 0; + u8 *buf; + u32 val; + u32 host_reg[] = { + (UFS_PHY_MNG_BASE + UFS_MPHY_RST_CTRL), + (UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL), + (UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL), + 0xFFF, + }; + + buf = kzalloc(VENDOR_DUMP_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return; + + len += scnprintf(buf + len, VENDOR_DUMP_BUF_SIZE - len, "vendor specific registers:"); + + for (offset = 0xC0, i = 0; offset < VENDOR_MAX_OFFSET; offset += 4, i++) { + val = ufshcd_readl(hba, offset); + if (i % 4 == 0) + len += scnprintf(buf + len, VENDOR_DUMP_BUF_SIZE - len, "\n"); + len += scnprintf(buf + len, VENDOR_DUMP_BUF_SIZE - len, " 0x%03x: 0x%08x ", + offset, val); + } + + len += scnprintf(buf + len, VENDOR_DUMP_BUF_SIZE - len, "\n mphy and atop registers:"); + + for (i = 0; host_reg[i] != 0xFFF; i++) { + val = ufshcd_readl(hba, host_reg[i]); + if (i % 4 == 0) + len += scnprintf(buf + len, VENDOR_DUMP_BUF_SIZE - len, "\n"); + len += scnprintf(buf + len, VENDOR_DUMP_BUF_SIZE - len, " 0x%03x: 0x%08x ", + host_reg[i], val); + } + len += scnprintf(buf + len, VENDOR_DUMP_BUF_SIZE - len, "\n"); + + dev_warn(hba->dev, "%s", buf); + + kfree(buf); +} + +/* M-PHY FSM states */ +#define MPHY_RX_FSM_STATE 0xC1 +#define MPHY_TX_FSM_STATE 0x41 + +static bool is_fsm_state_valid(u32 state) +{ + return (state == FSM_STATE_ACTIVE || state == FSM_STATE_LS_BURST); +} + +static int ufs_spacemit_check_hibern8(struct ufs_hba *hba) +{ + u32 tx_fsm_val_0 = 0; + u32 tx_fsm_val_1 = 0; + int retries = DIV_ROUND_UP(HBRN8_POLL_TOUT_MS * 1000, 100); + int err = 0; + + do { + err = ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), + &tx_fsm_val_0); + err |= ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), + &tx_fsm_val_1); + if (err || (tx_fsm_val_0 == TX_FSM_HIBERN8 && + tx_fsm_val_1 == TX_FSM_HIBERN8)) + break; + + usleep_range(100, 200); + } while (--retries > 0); + + if (!err && retries <= 0) { + err = ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), + &tx_fsm_val_0); + err |= ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), + &tx_fsm_val_1); + } + + if (err) { + dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", + __func__, err); + } else if (tx_fsm_val_0 != TX_FSM_HIBERN8 || + tx_fsm_val_1 != TX_FSM_HIBERN8) { + err = -ETIMEDOUT; + dev_err(hba->dev, + "%s: invalid TX_FSM_STATE, lane0 = %u, lane1 = %u\n", + __func__, tx_fsm_val_0, tx_fsm_val_1); + } + + return err; +} + +static void ufs_spacemit_dump_fsm_state(struct ufs_hba *hba) +{ + u32 tx0_fsm_val, tx1_fsm_val, rx0_fsm_val, rx1_fsm_val; + int err; + + err = ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), + &tx0_fsm_val); + if (err) + return; + + usleep_range(100, 200); + + err = ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), + &tx1_fsm_val); + if (err) + return; + + usleep_range(100, 200); + + err = ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_RX_FSM_STATE, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + &rx0_fsm_val); + if (err) + return; + + usleep_range(100, 200); + + err = ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_RX_FSM_STATE, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), + &rx1_fsm_val); + if (err) + return; + + usleep_range(100, 200); + + if (!is_fsm_state_valid(tx0_fsm_val) || !is_fsm_state_valid(tx1_fsm_val) || + !is_fsm_state_valid(rx0_fsm_val) || !is_fsm_state_valid(rx1_fsm_val)) { + dev_warn(hba->dev, "FSM state invalid - TX:[0x%x, 0x%x], RX:[0x%x, 0x%x]\n", + tx0_fsm_val, tx1_fsm_val, rx0_fsm_val, rx1_fsm_val); + } +} + +static int ufs_spacemit_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) +{ + int err; + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), tx_lanes); + if (err) + dev_err(hba->dev, "%s: couldn't read PA_CONNECTEDTXDATALANES %d\n", __func__, err); + + return err; +} + +static u32 ufs_spacemit_get_sys1clk_1us(struct ufs_hba *hba) +{ + struct ufs_clk_info *clki, *ufs_aclk = NULL; + struct list_head *head = &hba->clk_list_head; + unsigned long rate_hz = 0; + + if (!list_empty(head)) { + list_for_each_entry(clki, head, list) { + if (clki->name && !strcmp(clki->name, "aclk") && clki->clk) { + ufs_aclk = clki; + break; + } + } + } + + if (ufs_aclk && ufs_aclk->clk) + rate_hz = clk_get_rate(ufs_aclk->clk); + + if (!rate_hz) + return 0; + + return DIV_ROUND_CLOSEST(rate_hz, 1000000); +} + +static int ufs_spacemit_wait_mphy_pll_lock(struct ufs_hba *hba) +{ + int timeout = MPHY_PLL_LOCK_TIMEOUT_US; + u32 val; + + while (timeout-- > 0) { + val = ufshcd_readl(hba, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + if (val & MPHY_PLL_LOCK_BIT) + return 0; + + udelay(1); + } + + dev_err(hba->dev, "M-PHY PLL lock timeout\n"); + return -ETIMEDOUT; +} + +/** + * ufs_spacemit_mphy_init + * @hba: host controller instance + */ +static int ufs_spacemit_mphy_init(struct ufs_hba *hba) +{ + int ret; + + /* reset all mphy logical */ + ufshcd_writel(hba, 0x003, UFS_PHY_MNG_BASE + 0x0); + mdelay(1); + + /* power up all */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + 0x4); + mdelay(1); + + /* asserted ana_rx_hb8_reset */ + ufshcd_writel(hba, 0xb7f, UFS_PHY_MNG_BASE + 0x4); + mdelay(1); + + /* deasserted ana_rx_hb8_reset */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + 0x4); + mdelay(1); + + /* deasserted ufs device reset & refer clk output enable */ + ufshcd_writel(hba, 0x101, UFS_PHY_MNG_BASE + 0xC); + mdelay(1); + + ret = ufs_spacemit_wait_mphy_pll_lock(hba); + if (ret < 0) + return ret; + + dev_dbg(hba->dev, "M-PHY PLL locked successfully\n"); + + ufshcd_writel(hba, 0x1, UFS_PHY_MNG_BASE + 0x08); + udelay(5); + + ufshcd_writel(hba, 0x40, UFS_ATOP_BASE + (0xC2 << 2)); + udelay(5); + + ufshcd_writel(hba, 0x0, UFS_PHY_MNG_BASE + 0x08); + + mdelay(2); + + dev_dbg(hba->dev, "M-PHY init completed\n"); + + return 0; +} + +static int ufs_spacemit_uniprov1p6_init(struct ufs_hba *hba) +{ + /* PA_TXHSG1SYNCLENGTH */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1552), 0x4f); + + /* PA_TXHSG1PREPARELENGTH */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1553), 0xf); + + /* PA_TXHSG2SYNCLENGTH */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1554), 0x4f); + + /* PA_TXHSG2PREPARELENGTH */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1555), 0xf); + + /* PA_TXHSG3SYNCLENGTH */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1556), 0x4f); + + /* PA_TXHSG3PREPARELENGTH */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1557), 0xf); + + /* PA_TXMK2EXTENSION */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x155A), 0x0); + + /* PA_PEERSCRAMBLING */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x155B), 0x1); + + /* PA_TXSKIP */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x155C), 0x1); + + /* PA_TXSKIPPERIOD */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x155D), 250); + + /* PA_LOCAL_TX_LCC_ENABLE */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x155E), 0x0); + + /* PA_PEER_TX_LCC_ENABLE */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x155F), 0x0); + + /* PA_SCRAMBLING */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1585), 0x1); + + /* PA_GRANULARITY */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x15AA), 0x1); + + /* PA_MK2EXTENSIONGUARDBAND */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x15AB), 0x0); + + /* PA_STALLNOCONFIGTIME */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x15A3), 15); + + /* PA_TACTIVATE */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x15A8), 0x64); + + /* PA_TXTRAILINGCLOCKS */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1564), 0x64); + + /* RX_LS_PREPARELEN_TIME RX0 */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008D, 4), 0x0B); + + /* RX_LS_PREPARELEN_TIME RX1 */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008D, 5), 0X0B); + + /* RX_HIBERNATE_BKEN RX0 */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x00F4, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x9F); + + /* RX_HIBERNATE_BKEN RX1 */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x00F4, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x9F); + + /* PWM_BURST_closure_length */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008E, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 15); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008E, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 15); + + /* min_stall_not_config_time*/ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0088, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0088, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0xFF); + + /* TX HB8_TIME CAP */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x000F, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x64); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x000F, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), 0x64); + + /*RX HB8_TIME CAP*/ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0092, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x64); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0092, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x64); + + /*TX EQ 3DB*/ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x00CD, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x5); + + /*RX garbage cnt = 32 SI*/ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x00F2, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x9F); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x00F2, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x9F); + + dev_info(hba->dev, "UniPro v1.6 init completed\n"); + + return 0; +} + +static void ufs_spacemit_set_dev_cap(struct ufs_host_params *host_params, u32 pwr_hs) +{ + if (!host_params) + return; + + memset(host_params, 0, sizeof(struct ufs_host_params)); + host_params->tx_lanes = UFS_SPACEMIT_K3_LIMIT_NUM_LANES_TX; + host_params->rx_lanes = UFS_SPACEMIT_K3_LIMIT_NUM_LANES_RX; + host_params->hs_rx_gear = UFS_SPACEMIT_K3_LIMIT_HSGEAR_RX; + host_params->hs_tx_gear = UFS_SPACEMIT_K3_LIMIT_HSGEAR_TX; + host_params->pwm_rx_gear = UFS_SPACEMIT_K3_LIMIT_PWMGEAR_RX; + host_params->pwm_tx_gear = UFS_SPACEMIT_K3_LIMIT_PWMGEAR_TX; + host_params->rx_pwr_pwm = UFS_SPACEMIT_K3_LIMIT_RX_PWR_PWM; + host_params->tx_pwr_pwm = UFS_SPACEMIT_K3_LIMIT_TX_PWR_PWM; + host_params->rx_pwr_hs = pwr_hs; + host_params->tx_pwr_hs = pwr_hs; + host_params->hs_rate = UFS_SPACEMIT_K3_LIMIT_HS_RATE; + host_params->desired_working_mode = UFS_HS_MODE; +} + +static int ufs_spacemit_link_startup_pre_change(struct ufs_hba *hba) +{ + u32 value, sys1clk_1us; + + ufs_spacemit_mphy_init(hba); + + ufs_spacemit_uniprov1p6_init(hba); + + /* config sysclk and tx symbol clk before link startup */ + value = UFS_MAX_LINKSTARTUP_TIMER; + + /* clear bit0~bit3, select b0 design */ + value &= ~0xf; + + ufshcd_writel(hba, value, UFS_PA_LINK_STARTUP_TIMER); + + sys1clk_1us = ufs_spacemit_get_sys1clk_1us(hba); + if (!sys1clk_1us) + sys1clk_1us = DIV_ROUND_CLOSEST(UFS_ACLK_LOW_FREQ_HZ, 1000000); + ufshcd_writel(hba, sys1clk_1us, UFS_SYS1CLK_1US); + ufshcd_writel(hba, UFS_TX_SYMBO_CLK, UFS_TX_SYMBOL_CLK_NS_US); + + dev_dbg(hba->dev, "REG_UFS_SYS1CLK_1US: 0x%x\n", ufshcd_readl(hba, UFS_SYS1CLK_1US)); + dev_dbg(hba->dev, "REG_UFS_TX_SYMBOL_CLK_NS_US: 0x%x\n", + ufshcd_readl(hba, UFS_TX_SYMBOL_CLK_NS_US)); + + return 0; +} + +static int ufs_spacemit_link_startup_post_change(struct ufs_hba *hba) +{ + u32 tx_lanes; + + /* add 0xe8 make UFS2.1 run GEAR3 + 2Lane@409M */ + mdelay(5); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xe8, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x97); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xe8, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0xd7); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xe8, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x17); + + ufshcd_dme_set(hba, UIC_ARG_MIB(DL_AFC0REQTIMEOUTVAL), UFS_DL_AFC0REQTIMEOUTVAL_MAX); + + return ufs_spacemit_get_connected_tx_lanes(hba, &tx_lanes); +} + +static int ufs_spacemit_link_startup_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) +{ + switch (status) { + case PRE_CHANGE: + ufs_spacemit_link_startup_pre_change(hba); + break; + case POST_CHANGE: + ufs_spacemit_link_startup_post_change(hba); + break; + default: + break; + } + + return 0; +} + +static int ufs_spacemit_negotiate_pwr_mode(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_host_params host_params; + int ret; + + ufs_spacemit_set_dev_cap(&host_params, FAST_MODE); + ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, + dev_req_params); + if (ret) { + dev_err(hba->dev, "Failed to negotiate power params: %d\n", ret); + return ret; + } + + dev_dbg(hba->dev, + "Power mode config - gear_rx:%d, gear_tx:%d, lane_rx:%d, lane_tx:%d, pwr_rx:%d, pwr_tx:%d, hs_rate:%d\n", + dev_req_params->gear_rx, dev_req_params->gear_tx, dev_req_params->lane_rx, + dev_req_params->lane_tx, dev_req_params->pwr_rx, dev_req_params->pwr_tx, + dev_req_params->hs_rate); + + return ret; +} + +static int ufs_spacemit_pwr_change_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_spacemit_host *host = ufshcd_get_variant(hba); + int ret = 0; + + if (!dev_req_params) { + dev_err(hba->dev, "Invalid Parameters\n"); + return -EINVAL; + } + + switch (status) { + case PRE_CHANGE: + break; + case POST_CHANGE: + /* cache the power mode parameters to use internally */ + memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); + ret = ufs_spacemit_wait_mphy_pll_lock(hba); + if (ret < 0) + return ret; + + dev_dbg(hba->dev, "M-PHY PLL locked after power mode change\n"); + /*set ANA_HSGEAR_CTRL_ATTR back to default value*/ + ufshcd_dme_set(hba, UIC_ARG_MIB(ANA_HSGEAR_CTRL_ATTR), 0x00); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int ufs_spacemit_quirk_host_pa_saveconfigtime(struct ufs_hba *hba) +{ + int err; + u32 pa_vs_config_reg1; + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(UFS_PA_VS_CONFIG_REG1), &pa_vs_config_reg1); + if (err) + return err; + + /* Allow extension of MSB bits of PA_SaveConfigTime attribute */ + return ufshcd_dme_set(hba, UIC_ARG_MIB(UFS_PA_VS_CONFIG_REG1), + (pa_vs_config_reg1 | (1 << 12))); +} + +static int ufs_spacemit_apply_dev_quirks(struct ufs_hba *hba) +{ + if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME) + ufs_spacemit_quirk_host_pa_saveconfigtime(hba); + + if (hba->dev_info.wmanufacturerid == UFS_VENDOR_WDC) + hba->dev_quirks |= UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE; + + /*LCC_DISABLE*/ + mdelay(50); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_LCC_ENABLE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_LCC_ENABLE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), 0); + + /*TX_Min_ActivateTime*/ + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_MIN_ACTIVATETIME, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), + 0x0); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_MIN_ACTIVATETIME, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), + 0x0); + mdelay(10); + + /*use backdoor reg to pre-set TX RATE/GEAR to let PLL lock before set_power_mode switch*/ + ufshcd_dme_set(hba, UIC_ARG_MIB(ANA_HSGEAR_CTRL_ATTR), 0x25); + mdelay(10); + + return ufs_spacemit_wait_mphy_pll_lock(hba); +} + +/** + * ufs_spacemit_advertise_quirks - advertise the known Spacemit UFS controller quirks + * @hba: host controller instance + * + * Spacemit UFS host controller might have some non standard behaviours (quirks) + * than what is specified by UFSHCI specification. Advertise all such + * quirks to standard UFS host controller driver so standard takes them into + * account. + */ +static void ufs_spacemit_advertise_quirks(struct ufs_hba *hba) +{ + hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; +} + +/** + * ufs_spacemit_fsm_dump_work - Deferred work to dump FSM state + * @work: work structure + * + * This function is called from a workqueue context (not interrupt context), + * allowing safe execution of blocking operations like ufshcd_dme_get(). + */ +static void ufs_spacemit_fsm_dump_work(struct work_struct *work) +{ + struct ufs_spacemit_host *host = container_of(work, struct ufs_spacemit_host, + fsm_dump_work); + struct ufs_hba *hba = host->hba; + + /* Safe to call blocking functions in workqueue context */ + if (ufshcd_is_link_active(hba)) + ufs_spacemit_dump_fsm_state(hba); +} + +/** + * ufs_spacemit_init - init phy and prepare clk + * @hba: host controller instance + */ +static int ufs_spacemit_init(struct ufs_hba *hba) +{ + int err = 0; + struct device *dev = hba->dev; + struct ufs_spacemit_host *host; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + host->rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(host->rst)) + return dev_err_probe(dev, PTR_ERR(host->rst), + "Failed to get reset control\n"); + + host->hba = hba; + ufshcd_set_variant(hba, host); + ufs_spacemit_advertise_quirks(hba); + + /* Initialize workqueue for deferred FSM state dump */ + INIT_WORK(&host->fsm_dump_work, ufs_spacemit_fsm_dump_work); + + err = ufshcd_vops_phy_initialization(host->hba); + return err; +} + +/** + * ufs_spacemit_device_reset - Toggle device reset line + * @hba: per-adapter instance + * + * Toggles the reset line to reset the attached UFS device. + * + * Returns: 0 on success + */ +static int ufs_spacemit_device_reset(struct ufs_hba *hba) +{ + /* Stop device ref_clk & asserted ufs device reset */ + ufshcd_writel(hba, 0x000, UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + mdelay(1); + + /* Enable device ref_clk & de-asserted ufs device reset */ + ufshcd_writel(hba, MPHY_DEVICE_RESET_DEASSERT, UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + mdelay(1); + + return 0; +} + +/** + * ufs_spacemit_event_notify - Handle UFS error events + * @hba: host controller instance + * @evt: event type + * @data: event-specific data + * + * Handles error events from UFS core, dumps registers immediately + * and schedules FSM state dump for later execution in workqueue context. + */ +static void ufs_spacemit_event_notify(struct ufs_hba *hba, enum ufs_event_type evt, void *data) +{ + struct ufs_spacemit_host *host = ufshcd_get_variant(hba); + bool dump_regs = false; + + switch (evt) { + case UFS_EVT_PA_ERR: + if (data) + dev_warn(hba->dev, "PA error event, INT errors:0x%x, PA_ERR_CODE:0x%x\n", + hba->errors, *(u32 *)data); + + dump_regs = true; + break; + + case UFS_EVT_DL_ERR: + if (data) + dev_warn(hba->dev, "DL error event, INT errors:0x%x, DL_ERR:0x%x\n", + hba->errors, *(u32 *)data); + + dump_regs = true; + break; + + case UFS_EVT_ABORT: + dev_warn(hba->dev, "Abort event, INT errors:0x%x\n", hba->errors); + break; + + default: + break; + } + + /* Dump registers if error occurred (safe in interrupt context) */ + if (hba->errors || dump_regs) + ufs_spacemit_dump_host_regs(hba); + + /* Schedule FSM state dump in workqueue context (not in interrupt context) */ + if (ufshcd_is_link_active(hba) && host) + queue_work(system_wq, &host->fsm_dump_work); +} + +/** + * ufs_spacemit_hibern8_notify - Handle hibernate enter/exit + * @hba: host controller instance + * @cmd: UIC command (HIBER_ENTER or HIBER_EXIT) + * @status: notification status + * + * Manages M-PHY power state during hibernate transitions. + */ +static void ufs_spacemit_hibern8_notify(struct ufs_hba *hba, enum uic_cmd_dme cmd, + enum ufs_notify_change_status status) +{ + int ret; + + dev_dbg(hba->dev, "Hibern8 notify: cmd=%d, status=%d\n", cmd, status); + if (status == PRE_CHANGE) { + if (cmd == UIC_CMD_DME_HIBER_EXIT) { + mdelay(1); + + /* Enable reference clock */ + ufshcd_writel(hba, MPHY_DEVICE_RESET_DEASSERT, + UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + mdelay(1); + + /* Power up all */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + mdelay(1); + + /* Assert ana_rx_hb8_reset */ + ufshcd_writel(hba, MPHY_PU_WITH_HB8_RESET, + UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + mdelay(1); + + /* Deassert ana_rx_hb8_reset */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + + ret = ufs_spacemit_wait_mphy_pll_lock(hba); + if (ret < 0) + return; + + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xdd), 0x57); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xe8), 0x57); + } + } + + if (status == PRE_CHANGE) { + if (cmd == UIC_CMD_DME_HIBER_ENTER) { + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + 0x84); + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), + 0x84); + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + 0x85); + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), + 0x85); + } + } + + if (status == POST_CHANGE) { + if (cmd == UIC_CMD_DME_HIBER_ENTER) { + ufs_spacemit_check_hibern8(hba); + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + 0x84); + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), + 0x84); + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + 0x80); + ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), + 0x80); + + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xdd), 0x57); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xdd), 0xd7); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xe8), 0x57); + mdelay(1); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xe8), 0xd7); + mdelay(1); + + /* Power down M-PHY */ + ufshcd_writel(hba, 0x0, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + mdelay(1); + + /* Keep reference clock enabled, assert device reset */ + ufshcd_writel(hba, MPHY_DEVICE_RESET_ASSERT, + UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + } + } +} + +/** + * ufs_spacemit_hce_enable_notify - Configure HCE enable sequence + * @hba: host controller instance + * @status: notification status (PRE_CHANGE or POST_CHANGE) + * + * Configures host controller enable with proper sequencing. + * Handles crypto enable if supported. + * + * Returns: 0 on success + */ +static int ufs_spacemit_hce_enable_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) +{ + struct ufs_spacemit_host *host = ufshcd_get_variant(hba); + u32 enable_val, val; + + if (status == PRE_CHANGE) { + enable_val = CONTROLLER_ENABLE; + + if (hba->caps & UFSHCD_CAP_CRYPTO) + enable_val = CRYPTO_GENERAL_ENABLE | CONTROLLER_ENABLE; + + if (!host->first_hce_done) { + host->first_hce_done = true; + dev_dbg(hba->dev, "First HCE enable\n"); + } else { + val = ufshcd_readl(hba, REG_CONTROLLER_ENABLE); + if (val == enable_val) { + ufshcd_writel(hba, enable_val & (1 << CONTROLLER_ENABLE), + REG_CONTROLLER_ENABLE); + + while (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) == + (enable_val & (1 << CONTROLLER_ENABLE))) + ; + } + } + } + + return 0; +} + +/** + * struct ufs_hba_spacemit_vops - UFS Spacemit specific variant operations + * + * The variant operations configure the necessary controller and PHY + * handshake during initialization. + */ +static const struct ufs_hba_variant_ops ufs_hba_spacemit_vops = { + .name = "ufshcd-spacemit", + .init = ufs_spacemit_init, + .link_startup_notify = ufs_spacemit_link_startup_notify, + .negotiate_pwr_mode = ufs_spacemit_negotiate_pwr_mode, + .pwr_change_notify = ufs_spacemit_pwr_change_notify, + .device_reset = ufs_spacemit_device_reset, + .event_notify = ufs_spacemit_event_notify, + .apply_dev_quirks = ufs_spacemit_apply_dev_quirks, + .hibern8_notify = ufs_spacemit_hibern8_notify, + .hce_enable_notify = ufs_spacemit_hce_enable_notify, +}; + +static const struct of_device_id ufs_spacemit_of_match[] = { + { .compatible = "spacemit,k3-ufshc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ufs_spacemit_of_match); + +static int ufs_spacemit_probe(struct platform_device *pdev) +{ + return ufshcd_pltfrm_init(pdev, &ufs_hba_spacemit_vops); +} + +static void ufs_spacemit_remove(struct platform_device *pdev) +{ + ufshcd_pltfrm_remove(pdev); +} + +static struct platform_driver ufs_spacemit_pltform = { + .probe = ufs_spacemit_probe, + .remove = ufs_spacemit_remove, + .driver = { + .name = "ufshcd-spacemit", + .of_match_table = of_match_ptr(ufs_spacemit_of_match), + }, +}; +module_platform_driver(ufs_spacemit_pltform); + +MODULE_DESCRIPTION("SpacemiT UFS Host Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ufs/host/ufs-spacemit.h b/drivers/ufs/host/ufs-spacemit.h new file mode 100644 index 00000000000000..6ae3c263a3601f --- /dev/null +++ b/drivers/ufs/host/ufs-spacemit.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SpacemiT UFS Host Controller driver + * + * Copyright (c) 2026 SpacemiT (Hangzhou) Technology Co. Ltd + */ + +#ifndef _UFS_SPACEMIT_H_ +#define _UFS_SPACEMIT_H_ + +#include +#include +#include +#include +#include + +/* Spacemit K3 UFS host controller vendor specific registers */ +#define UFS_SYS1CLK_1US 0xC0 +#define UFS_TX_SYMBOL_CLK_NS_US 0xC4 +#define UFS_LOCAL_PORT_ID_REG 0xC8 +#define UFS_PA_ERR_CODE 0xCC +#define UFS_RETRY_TIMER_REG 0xD0 +#define UFS_PA_LINK_STARTUP_TIMER 0xD8 +#define UFS_CFG1 0xDC + +/* MPHY control registers */ +#define UFS_PHY_MNG_BASE 0x1B00 +#define UFS_MPHY_RST_CTRL 0x0 +#define UFS_MPHY_PU_CTRL 0x4 +#define UFS_MPHY_BKDR_CTRL 0x8 +#define UFS_DEVICE_IO_CTRL 0xC + +/* ATOP base*/ +#define UFS_ATOP_BASE 0x1C00 + +#define UFS_SYSCLK 499 +#define UFS_TX_SYMBO_CLK 0x800 +#define UFS_MAX_LINKSTARTUP_TIMER 0xFFFFFFFF +#define UFS_DL_AFC0REQTIMEOUTVAL_MAX 0xFFFF + +#define MPHY_TX_FSM_STATE 0x41 +#define TX_FSM_HIBERN8 0x1 +#define HBRN8_POLL_TOUT_MS 100 +#define DEFAULT_CLK_RATE_HZ 1000000 +#define BUS_VECTOR_NAME_LEN 32 + +#define UFS_HW_VER_MAJOR_SHFT 28 +#define UFS_HW_VER_MAJOR_MASK (0x000F << UFS_HW_VER_MAJOR_SHFT) +#define UFS_HW_VER_MINOR_SHFT 16 +#define UFS_HW_VER_MINOR_MASK (0x0FFF << UFS_HW_VER_MINOR_SHFT) +#define UFS_HW_VER_STEP_SHFT 0 +#define UFS_HW_VER_STEP_MASK (0xFFFF << UFS_HW_VER_STEP_SHFT) + +#define UFS_SPACEMIT_K3_LIMIT_NUM_LANES_RX 2 +#define UFS_SPACEMIT_K3_LIMIT_NUM_LANES_TX 2 +#define UFS_SPACEMIT_K3_LIMIT_HSGEAR_RX UFS_HS_G3 +#define UFS_SPACEMIT_K3_LIMIT_HSGEAR_TX UFS_HS_G3 +#define UFS_SPACEMIT_K3_LIMIT_PWMGEAR_RX UFS_PWM_G4 +#define UFS_SPACEMIT_K3_LIMIT_PWMGEAR_TX UFS_PWM_G4 +#define UFS_SPACEMIT_K3_LIMIT_RX_PWR_PWM SLOW_MODE +#define UFS_SPACEMIT_K3_LIMIT_TX_PWR_PWM SLOW_MODE +#define UFS_SPACEMIT_K3_LIMIT_RX_PWR_HS FAST_MODE +#define UFS_SPACEMIT_K3_LIMIT_TX_PWR_HS FAST_MODE +#define UFS_SPACEMIT_K3_LIMIT_HS_RATE PA_HS_MODE_B +#define UFS_SPACEMIT_K3_LIMIT_DESIRED_MODE 2 + +#define UFS_PA_VS_CONFIG_REG1 0x9000 +#define UFS_DME_VS_CORE_CLK_CTRL 0xD002 + +#define UFS_HCLKDIV_REG 0xFC + +struct ufs_spacemit_host { + struct ufs_hba *hba; + struct ufs_pa_layer_attr dev_req_params; + struct reset_control *rst; + u32 caps; + u32 lpm_qos; + u32 unipro_ver; + u32 remote_unipro_ver; + u8 prev_request_crypto; + + bool is_lane_clks_on; + bool first_init_done; + bool first_hce_done; + + /* Workqueue for deferred FSM state dump */ + struct work_struct fsm_dump_work; +}; + +#endif /* _UFS_SPACEMIT_H_ */ From b2b8d2c1fc4ef2c7914e6df51b91fd4983fafa76 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 2 Jul 2026 02:31:37 +0000 Subject: [PATCH 254/521] FROMLIST: riscv: dts: spacemit: k3: Add UFS support Add UFS Host Controller support for SpacemiT K3 SoC, and enable it both on both Pico-ITX and CoM260-IFX boards. Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20260702-08-k3-ufs-support-v1-3-1a64a3ab128f@kernel.org Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts | 4 ++++ arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 4 ++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 13 +++++++++++++ 3 files changed, 21 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts b/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts index e7f76cd24b9032..91bc8c0a2ed9d0 100644 --- a/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-com260-ifx.dts @@ -37,3 +37,7 @@ sd-uhs-sdr104; status = "okay"; }; + +&ufshc { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index 8b2a120b02d15f..c7ecb2f5cb4d7b 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -317,3 +317,7 @@ &usb2_phy { status = "okay"; }; + +&ufshc { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index fc46e34465e479..862e5698972e76 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -1216,5 +1216,18 @@ riscv,num-sources = <512>; status = "reserved"; }; + + ufshc: ufshc@c0e00000 { + compatible = "spacemit,k3-ufshc"; + reg = <0x0 0xc0e00000 0x0 0x40000>; + clocks = <&syscon_apmu CLK_APMU_UFS_ACLK>, + <&syscon_apmu CLK_APMU_UFS_REFCLK>; + clock-names = "aclk", "ref_clk"; + resets = <&syscon_apmu RESET_APMU_UFS_ACLK>; + interrupts = <135 IRQ_TYPE_LEVEL_HIGH>; + freq-table-hz = <491520000 491520000 19200000 19200000>; + lanes-per-direction = <2>; + status = "disabled"; + }; }; }; From b9dbe60409d5dda2974c48795b2b5daa12f40a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 30 Jun 2026 09:41:40 +0200 Subject: [PATCH 255/521] FROMLIST: riscv: vdso: Add symbols for the alternative section boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the alternative sections are extracted from the vDSO binaries at runtime. This has runtime overhead and also doesn't work for the compat vDSO. Extract the offsets of the alternative section during the build with the existing symbol extraction machinery. Also add dummy symbols as fallback. Signed-off-by: Thomas Weißschuh Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260630-riscv-vdso32-alternative-v1-1-a32fd89b7b1c@linutronix.de Signed-off-by: Han Gao --- arch/riscv/include/asm/vdso.h | 6 ++++++ arch/riscv/kernel/vdso/vdso.lds.S | 2 ++ 2 files changed, 8 insertions(+) diff --git a/arch/riscv/include/asm/vdso.h b/arch/riscv/include/asm/vdso.h index f7998d9ad9b2d7..0259bbc98e93a1 100644 --- a/arch/riscv/include/asm/vdso.h +++ b/arch/riscv/include/asm/vdso.h @@ -19,10 +19,14 @@ #ifdef CONFIG_MMU #include +#else +extern size_t __vdso_alternatives_start_offset, __vdso_alternatives_end_offset; #endif #ifdef CONFIG_RISCV_USER_CFI #include +#else +extern size_t __vdso_alternatives_start_cfi_offset, __vdso_alternatives_end_cfi_offset; #endif #ifdef CONFIG_RISCV_USER_CFI @@ -41,6 +45,8 @@ #define COMPAT_VDSO_SYMBOL(base, name) \ (void __user *)((unsigned long)(base) + compat__vdso_##name##_offset) +#else +extern size_t compat__vdso_alternatives_start_offset, compat__vdso_alternatives_end_offset; #endif /* CONFIG_COMPAT */ extern char vdso_start[], vdso_end[]; diff --git a/arch/riscv/kernel/vdso/vdso.lds.S b/arch/riscv/kernel/vdso/vdso.lds.S index c29ef12a63bb43..5e9424df932b70 100644 --- a/arch/riscv/kernel/vdso/vdso.lds.S +++ b/arch/riscv/kernel/vdso/vdso.lds.S @@ -45,9 +45,11 @@ SECTIONS .text : { *(.text .text.*) } :text . = ALIGN(4); + __vdso_alternatives_start = .; .alternative : { *(.alternative) } + __vdso_alternatives_end = .; } /* From 078c144230d042b0ebb3217b0c61bb67018a72b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 30 Jun 2026 09:41:41 +0200 Subject: [PATCH 256/521] FROMLIST: riscv: alternative: Use the statically extracted vDSO section offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the alternative sections are extracted from the vDSO binaries at runtime. This has runtime overhead and also doesn't work for the compat vDSO. Use the offsets generated during the build instead, fixing both issues. Signed-off-by: Thomas Weißschuh Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260630-riscv-vdso32-alternative-v1-2-a32fd89b7b1c@linutronix.de Signed-off-by: Han Gao --- arch/riscv/kernel/alternative.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 104dc0862c5c2e..6aba169fb21362 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,24 +173,14 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -static void __init apply_vdso_alternatives(void *start) +static __always_inline +void __init apply_vdso_alternatives(void *base, size_t alternatives_begin, size_t alternatives_end) { - const Elf_Ehdr *hdr; - const Elf_Shdr *shdr; - const Elf_Shdr *alt; - struct alt_entry *begin, *end; - - hdr = (Elf_Ehdr *)start; - shdr = (void *)hdr + hdr->e_shoff; - alt = find_section(hdr, shdr, ".alternative"); - if (!alt) + if (alternatives_begin == alternatives_end) return; - begin = (void *)hdr + alt->sh_offset, - end = (void *)hdr + alt->sh_offset + alt->sh_size, - - _apply_alternatives((struct alt_entry *)begin, - (struct alt_entry *)end, + _apply_alternatives(base + alternatives_begin, + base + alternatives_end, RISCV_ALTERNATIVES_BOOT); } @@ -204,10 +194,14 @@ void __init apply_boot_alternatives(void) RISCV_ALTERNATIVES_BOOT); if (IS_ENABLED(CONFIG_MMU)) - apply_vdso_alternatives(vdso_start); + apply_vdso_alternatives(vdso_start, + __vdso_alternatives_start_offset, + __vdso_alternatives_end_offset); if (IS_ENABLED(CONFIG_RISCV_USER_CFI)) - apply_vdso_alternatives(vdso_cfi_start); + apply_vdso_alternatives(vdso_cfi_start, + __vdso_alternatives_start_cfi_offset, + __vdso_alternatives_end_cfi_offset); } /* From 88946966889063355e4b691bd096b0af73eaef7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 30 Jun 2026 09:41:42 +0200 Subject: [PATCH 257/521] FROMLIST: riscv: alternative: Also patch the compat vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vDSO for compat processes can also contain alternative entries. Patch those, too. Signed-off-by: Thomas Weißschuh Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260630-riscv-vdso32-alternative-v1-3-a32fd89b7b1c@linutronix.de Signed-off-by: Han Gao --- arch/riscv/kernel/alternative.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 6aba169fb21362..c0c9306022c5f8 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -202,6 +202,11 @@ void __init apply_boot_alternatives(void) apply_vdso_alternatives(vdso_cfi_start, __vdso_alternatives_start_cfi_offset, __vdso_alternatives_end_cfi_offset); + + if (IS_ENABLED(CONFIG_COMPAT)) + apply_vdso_alternatives(compat_vdso_start, + compat__vdso_alternatives_start_offset, + compat__vdso_alternatives_end_offset); } /* From 73be66fc47f0973967f798e4edf9de6416b4b55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 1 Jul 2026 11:21:23 +0200 Subject: [PATCH 258/521] FROMLIST: riscv: vdso: Simplify cflags remove logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Listing all flags for each object file is tedious and error-prone. Replace it with a simpler solution. Link: https://lore.kernel.org/all/20260630135316-f26f0e0f-c08c-4d4d-9963-10f9985a7689@linutronix.de/ Signed-off-by: Thomas Weißschuh Reviewed-by: Nam Cao Link: https://lore.kernel.org/r/20260701-riscv-vdso-lto-v1-2-89db0cd82077@linutronix.de Signed-off-by: Han Gao --- arch/riscv/kernel/vdso/Makefile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 43ee881f6c6fd1..8dbf2532a57303 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -41,6 +41,8 @@ ccflags-y += $(CFI_FULL) asflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH) asflags-y += $(CFI_FULL) +ccflags-remove-y += $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) + ifneq ($(c-gettimeofday-y),) CFLAGS_vgettimeofday.o += -fPIC -include $(c-gettimeofday-y) endif @@ -68,11 +70,6 @@ ifneq ($(filter vgettimeofday, $(vdso-syms)),) CPPFLAGS_$(vdso_lds) += -DHAS_VGETTIMEOFDAY endif -# Disable -pg to prevent insert call site -CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) -CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) -CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) - # Force dependency $(obj)/$(vdso_o): $(obj)/$(vdso_so) From 0b3008167f410d3a1f1a93c703824112d1911d06 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 3 Jul 2026 10:10:22 +0800 Subject: [PATCH 259/521] FROMLIST: dt-bindings: phy: Add Spacemit K3 USB3/PCIe comb phy support The USB3/PCIe comb PHY on the K3 is a complex PHY group that can provide multiple phy for both PCIe and USB controller. Its mux configuration is controlled by the APMU syscon device. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260703021024.495433-2-inochiama@gmail.com Signed-off-by: Han Gao --- .../bindings/phy/spacemit,k3-combo-phy.yaml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml new file mode 100644 index 00000000000000..ed820cf697b822 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/spacemit,k3-combo-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spacemit K3 PCIE/USB3 Comb PHY + +maintainers: + - Inochi Amaoto + +properties: + compatible: + const: spacemit,k3-combo-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 2 + description: + The first one is phy id, the second one is phy type. This + only supports types are PCIe and USB . + + spacemit,apb-spare: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to APB SPARE system controller interface, used for + PHY calibration. + + spacemit,apmu: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle of APMU syscon + - description: configuration of the PHY lanes + description: | + Phandle to control PHY mux configuration. The configuration + is described as follows: + bit 4: 0 - PCIe A x8 mode, 1 - PCIe lane share mode + bit 3: 0 - PCIe A x4 mode, 1 - PCIe A x2 and PCIe B x2 mode + bit 2: 0 - PCIe C lane 0 is PCIe mode , 1 - USB mode + bit 1: 0 - PCIe C lane 1 is PCIe mode , 1 - USB mode + bit 0: 0 - PCIe D lane is PCIe mode , 1 - USB mode + + The bit[3:0] is only valid when bit 4 is 1. + + This mux control does not affect the PHY for PCIe E, it is always + in PCIe mode. + +required: + - compatible + - reg + - "#phy-cells" + - spacemit,apb-spare + - spacemit,apmu + +additionalProperties: false + +examples: + - | + phy@81d00000 { + compatible = "spacemit,k3-combo-phy"; + reg = <0x81d00000 0x600000>; + #phy-cells = <2>; + spacemit,apb-spare = <&apb_spare>; + spacemit,apmu = <&apmu 0x00>; + }; From a2246210da9368e14be4b575166de183ae12e450 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 3 Jul 2026 10:10:23 +0800 Subject: [PATCH 260/521] FROMLIST: phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3 The comb PHY on K3 requires to configure a syscon device for the right mux configuration. And it requires calibration before any usage. Add USB3/PCIe comb PHY driver for Spacemit K3. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260703021024.495433-3-inochiama@gmail.com Signed-off-by: Han Gao --- drivers/phy/spacemit/Kconfig | 16 ++ drivers/phy/spacemit/Makefile | 2 + drivers/phy/spacemit/phy-k3-combo.c | 252 +++++++++++++++++ drivers/phy/spacemit/phy-k3-common.c | 391 +++++++++++++++++++++++++++ drivers/phy/spacemit/phy-k3-common.h | 27 ++ 5 files changed, 688 insertions(+) create mode 100644 drivers/phy/spacemit/phy-k3-combo.c create mode 100644 drivers/phy/spacemit/phy-k3-common.c create mode 100644 drivers/phy/spacemit/phy-k3-common.h diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig index 50b0005acf6634..5fdf18fce4995c 100644 --- a/drivers/phy/spacemit/Kconfig +++ b/drivers/phy/spacemit/Kconfig @@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2 help Enable this to support K1 USB 2.0 PHY driver. This driver takes care of enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver. + +config PHY_SPACEMIT_K3_COMMON_OPS + tristate + select MFD_SYSCON + select GENERIC_PHY + +config PHY_SPACEMIT_K3_COMBO_PHY + tristate "SpacemiT K3 USB3/PCIe PHY support" + depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF + depends on COMMON_CLK + select PHY_SPACEMIT_K3_COMMON_OPS + help + Enable this to support K3 USB3/PCIe combo PHY driver. This + driver takes care of enabling and clock setup and will be used + by K3 dwc3 driver. + If unsure, say N. diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile index a821a21d614229..f679c1851b4f0b 100644 --- a/drivers/phy/spacemit/Makefile +++ b/drivers/phy/spacemit/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o +obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY) += phy-k3-combo.o +obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS) += phy-k3-common.o diff --git a/drivers/phy/spacemit/phy-k3-combo.c b/drivers/phy/spacemit/phy-k3-combo.c new file mode 100644 index 00000000000000..0bc08bb6247a80 --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-combo.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * phy-k3-combo.c - SpacemiT K3 PCIe/USB3 Combo PHY Driver + * + * Copyright (c) 2025 SpacemiT Technology Co. Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-k3-common.h" + +/* + * The PCIE/USB Subsystem on SpacemiT K3 have 3 single lane PIPE3 PHYs + * (PHY2/3/4) shared by PCIE PortC/D and USB3 PortB/C/D. + * + * PMUA_PCIE_SUBSYS_MGMT[4:0] + * + * bit4 = 0 : PCIe A X8 mode, all 8 lanes dedicated to PCIe Port A + * 1 : PHY lanes shared between PCIe or USB according to [3:0] + * + * All PHY matrix combinations according to [4:0]: + * + * 0x0X : PCIe-A X8 + * 0x10 : PCIe-C x2 (PHY2+PHY3) + PCIe-D x1 (PHY4) + * 0x11 : PCIe-C x2 (PHY2+PHY3) + USB-D (PHY4) + * 0x12 : PCIe-C x1 (PHY2) + USB-C (PHY3) + * 0x13 : PCIe-C x1 (PHY2) + USB-C (PHY3) + USB-D (PHY4) + * 0x14 : PCIe-C x1 (PHY3) + USB-B (PHY2) + * 0x15 : PCIe-C x1 (PHY3) + USB-B (PHY2) + USB-D (PHY4) + * 0x16 : USB-B (PHY2) + USB-C (PHY3) + PCIe D x1 (PHY4) + * 0x17 : USB-B (PHY2) + USB-C (PHY3) + USB-D (PHY4) + * + * So any USB Port B/C/D operation requires PCIe A X8 mode to be disabled. + */ +#define PMUA_PCIE_SUBSYS_MGMT 0x1d8 +#define PU_MATRIX_CONF_MASK GENMASK(4, 0) + +#define COMBPHY_MAX_SUBPHYS 6 + +struct k3_combo_phy { + struct device *dev; + struct k3_lane_group groups[COMBPHY_MAX_SUBPHYS]; + void __iomem *base; + struct regmap *apb_spare; +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group0 = { + .lanes = 2, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x0, 0x400 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group1 = { + .lanes = 2, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x100000, 0x100400 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group2 = { + .lanes = 1, + .config = 0x14, + .mask = 0x14, + .offsets = { + 0x200000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group3 = { + .lanes = 1, + .config = 0x12, + .mask = 0x12, + .offsets = { + 0x300000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group4 = { + .lanes = 1, + .config = 0x11, + .mask = 0x11, + .offsets = { + 0x400000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group5 = { + .lanes = 1, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x500000 + }, +}; + +static const struct k3_phy_lane_group_data *k3_combphy_lane_datas[] = { + &k3_combphy_lane_group0, + &k3_combphy_lane_group1, + &k3_combphy_lane_group2, + &k3_combphy_lane_group3, + &k3_combphy_lane_group4, + &k3_combphy_lane_group5, +}; + +static int k3_combo_phy_init_lanes(struct k3_combo_phy *phy, unsigned int config) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(k3_combphy_lane_datas); i++) { + const struct k3_phy_lane_group_data *data = k3_combphy_lane_datas[i]; + struct k3_lane_group *lg = &phy->groups[i]; + const struct phy_ops *ops; + bool is_usb; + + is_usb = (data->mask & config) == data->config; + if (is_usb) + ops = &k3_usb3_phy_ops; + else + ops = &k3_pcie_phy_ops; + + dev_dbg(phy->dev, "phy %d is %s\n", i, is_usb ? "usb" : "pcie"); + + lg->phy = devm_phy_create(phy->dev, NULL, ops); + if (IS_ERR(lg->phy)) + return PTR_ERR(lg->phy); + + lg->is_pcie = !is_usb; + lg->data = data; + lg->base = phy->base; + phy_set_drvdata(lg->phy, lg); + } + + return 0; +} + +static int k3_combo_phy_update_config(struct regmap *apmu, unsigned int config) +{ + if (config & ~PU_MATRIX_CONF_MASK) + return -EINVAL; + + return regmap_update_bits(apmu, PMUA_PCIE_SUBSYS_MGMT, PU_MATRIX_CONF_MASK, config); +} + +static struct phy *k3_combo_phy_xlate(struct device *dev, const struct of_phandle_args *args) +{ + struct k3_combo_phy *phy = dev_get_drvdata(dev); + struct k3_lane_group *lg; + + if (args->args_count != 2) { + dev_err(dev, "Invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + if (args->args[0] >= ARRAY_SIZE(k3_combphy_lane_datas)) { + dev_err(dev, "Invalid PHY id\n"); + return ERR_PTR(-EINVAL); + } + + lg = &phy->groups[args->args[0]]; + + if ((lg->is_pcie && args->args[1] != PHY_TYPE_PCIE) || + (!lg->is_pcie && args->args[1] != PHY_TYPE_USB3)) { + dev_err(dev, "Invalid PHY mode\n"); + return ERR_PTR(-EINVAL); + } + + return lg->phy; +} + +static int k3_combo_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct phy_provider *provider; + struct k3_combo_phy *phy; + struct regmap *apmu; + u32 config = 0; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->base)) + return PTR_ERR(phy->base); + + phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare"); + if (IS_ERR(phy->apb_spare)) + return dev_err_probe(dev, PTR_ERR(phy->apb_spare), + "Failed to find APB SPARE syscon"); + + apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config); + if (IS_ERR(apmu)) + return dev_err_probe(dev, PTR_ERR(apmu), + "Failed to find APMU syscon"); + + ret = k3_combo_phy_update_config(apmu, config); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to set lane configuration"); + + phy->dev = dev; + platform_set_drvdata(pdev, phy); + + ret = k3_phy_calibrate(phy->apb_spare); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to calibrate phy"); + + ret = k3_combo_phy_init_lanes(phy, config); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to init lanes"); + + provider = devm_of_phy_provider_register(dev, k3_combo_phy_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), + "Failed to register provider\n"); + + return 0; +} + +static const struct of_device_id k3_combo_phy_of_match[] = { + { .compatible = "spacemit,k3-combo-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, k3_combo_phy_of_match); + +static struct platform_driver k3_combo_phy_driver = { + .probe = k3_combo_phy_probe, + .driver = { + .name = "spacemit,k3-combo-phy", + .of_match_table = k3_combo_phy_of_match, + }, +}; +module_platform_driver(k3_combo_phy_driver); + +MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe combo PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c new file mode 100644 index 00000000000000..0b289e63db999a --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-common.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-k3-common.h" + +/* PHY Registers */ +#define PHY_VERSION 0x0 + +#define PHY_RESET_CFG 0x04 + +#define PHY_RESET_RXBUF_RST BIT(0) +#define PHY_RESET_SOFT_RST_PCS BIT(1) +#define PHY_RESET_SOFT_RST_AHB BIT(2) +#define PHY_RESET_EN_SD_AFTER_LOCK BIT(6) + +#define PHY_CLK_CFG 0x08 + +#define PHY_CLK_PLL_READY BIT(0) +#define PHY_CLK_TXCLK_INV BIT(2) +#define PHY_CLK_RXCLK_EN BIT(3) +#define PHY_CLK_TXCLK_EN BIT(4) +#define PHY_CLK_PCLK_EN BIT(5) +#define PHY_CLK_PIPE_PCLK_EN BIT(6) +#define PHY_CLK_REFCLK_FREQ GENMASK(10, 7) +#define PHY_CLK_REFCLK_24M 2 +#define PHY_CLK_SW_INIT_DONE BIT(11) +#define PHY_CLK_PU_SSC_OUT BIT(23) + +#define PHY_MODE_CFG 0x0C + +#define PHY_MODE_PCIE_INT_EN BIT(0) +#define PHY_MODE_LFPS_TPERIOD GENMASK(9, 8) +#define PHY_MODE_LFPS_TPERIOD_USB 3 + +#define PHY_PU_SEL 0x40 + +#define PHY_PU_CFG_STATUS BIT(9) +#define PHY_PU_OVRD_STATUS BIT(10) + +#define PHY_PU_CK_REG 0x54 + +#define PHY_PU_REFCLK_100 BIT(25) + +#define PHY_PLL_REG1 0x58 + +#define PHY_PLL_FREF_SEL GENMASK(15, 13) +#define PHY_PLL_FREF_24M 0x1 +#define PHY_PLL_SSC_DEP_SEL GENMASK(27, 24) +#define PHY_PLL_SSC_5000PPM 0xa +#define PHY_PLL_SSC_MODE GENMASK(29, 28) +#define PHY_PLL_SSC_MODE_CENTER_SPREAD 0 +#define PHY_PLL_SSC_MODE_UP_SPREAD 1 +#define PHY_PLL_SSC_MODE_DOWN_SPREAD 2 +#define PHY_PLL_SSC_MODE_DOWN_SPREAD1 3 + +#define PHY_PLL_REG2 0x5c + +#define PHY_PLL_SEL_REF100 BIT(21) + +/* PHY RX Register Definitions */ +#define PHY_RX_REG_A 0x60 + +#define PHY_RX_REG0_MASK GENMASK(7, 0) +#define PHY_RX_REG1_MASK GENMASK(15, 8) +#define PHY_RX_REG2_MASK GENMASK(23, 16) +#define PHY_RX_REG3_MASK GENMASK(31, 24) + +#define PHY_RX_REG0_RLOAD BIT(4) +#define PHY_RX_REG1_RTERM GENMASK(11, 8) +#define PHY_RX_REG1_RC_CALI GENMASK(15, 12) +#define PHY_RX_REG2_CSEL GENMASK(19, 16) +#define PHY_RX_REG2_FORCE_CSEL BIT(20) +#define PHY_RX_REG2_PSEL GENMASK(23, 21) +#define PHY_RX_REG3_I_LOAD GENMASK(26, 24) +#define PHY_RX_REG3_SEL_CBOOST_CODE BIT(27) +#define PHY_RX_REG3_ADJ_BIAS GENMASK(29, 28) +#define PHY_RX_REG3_RDEG1 GENMASK(31, 30) + +#define PHY_RX_REG_B 0x64 + +#define PHY_RX_REG4_MASK GENMASK(7, 0) +#define PHY_RX_REG5_MASK GENMASK(15, 8) +#define PHY_RX_REG6_MASK GENMASK(23, 16) + +#define PHY_RX_REGB_MASK GENMASK(23, 0) + +#define PHY_RX_REG4_RDEG2 GENMASK(2, 1) +#define PHY_RX_REG4_ENVOS BIT(4) +#define PHY_RX_REG4_RTERM_SEL BIT(5) +#define PHY_RX_REG4_MANUAL_CFG BIT(7) +#define PHY_RX_REG5_RCELL_VCM GENMASK(11, 8) +#define PHY_RX_REG5_RCELL_BIAS GENMASK(15, 12) +#define PHY_RX_REG6_H1_REG GENMASK(19, 16) +#define PHY_RX_REG6_ADAPT_GAIN GENMASK(21, 20) +#define PHY_RX_REG6_BYPASS_ADPT BIT(22) + +#define PHY_ADPT_CFG0 0x140 +#define PHY_ADPT_AFE_RST_OVRD_EN BIT(1) +#define PHY_ADPT_AFE_RST_OVRD_VAL BIT(4) + +#define PHY_RXEQ_TIME 0xb4 +#define PHY_RXEQ_TIME_OVRD_POST_C_SOC BIT(21) +#define PHY_RXEQ_TIME_CFG_AMP_SOC GENMASK(23, 22) +#define PHY_RXEQ_TIME_AMP_SOC_650M 0 +#define PHY_RXEQ_TIME_AMP_SOC_800M 1 +#define PHY_RXEQ_TIME_AMP_SOC_870M 2 +#define PHY_RXEQ_TIME_AMP_SOC_900M 3 +#define PHY_RXEQ_TIME_OVRD_AMP_SOC BIT(24) + +#define PCIE_PU_ADDR_CLK_CFG 0x0008 +#define PHY_CLK_PLL_READY BIT(0) +#define PCIE_INITAL_TIMER GENMASK(6, 3) +#define CFG_INTERNAL_TIMER_ADJ GENMASK(10, 7) +#define CFG_SW_PHY_INIT_DONE BIT(11) + +/* Lane RX/TX configuration (per‑lane, at lane_base) */ +#define PCIE_RX_REG1 0x050 +#define PCIE_RX_REFCLK_MODE GENMASK(1, 0) +#define PCIE_RX_REFCLK_MODE_DRIVER 1 +#define PCIE_RX_SEL_TRI_CODE BIT(2) +#define PCIE_RX_LEGACY GENMASK(15, 8) +#define PCIE_RX_LEGACY_DEFAULT 0x65 + +#define PCIE_TX_REG1 0x064 + +#define PCIE_PLL_TIMEOUT 500000 +#define PCIE_POLL_DELAY 500 + +static int k3_usb3phy_init_single(struct k3_lane_group *lg, void __iomem *base) +{ + struct phy *phy = lg->phy; + u32 val, tmp; + int ret; + + /* Do not wait CDR lock before sampling data */ + val = readl(base + PHY_RESET_CFG); + val = u32_replace_bits(val, 0, PHY_RESET_EN_SD_AFTER_LOCK); + writel(val, base + PHY_RESET_CFG); + + /* Power down 100MHz refclk buffer */ + val = readl(base + PHY_PU_CK_REG); + val = u32_replace_bits(val, 0, PHY_PU_REFCLK_100); + writel(val, base + PHY_PU_CK_REG); + + /* Program PLL REG1 configure the SSC */ + val = FIELD_PREP(PHY_PLL_SSC_MODE, PHY_PLL_SSC_MODE_DOWN_SPREAD1) | + FIELD_PREP(PHY_PLL_SSC_DEP_SEL, PHY_PLL_SSC_5000PPM) | + FIELD_PREP(PHY_PLL_FREF_SEL, PHY_PLL_FREF_24M); + writel(val, base + PHY_PLL_REG1); + + /* Un-select 100MHz PLL reference */ + val = readl(base + PHY_PLL_REG2); + val = u32_replace_bits(val, 0, PHY_PLL_SEL_REF100); + writel(val, base + PHY_PLL_REG2); + + /* USB LFPS period configuration */ + val = readl(base + PHY_MODE_CFG); + val = u32_replace_bits(val, PHY_MODE_LFPS_TPERIOD_USB, PHY_MODE_LFPS_TPERIOD); + writel(val, base + PHY_MODE_CFG); + + /* Force AFE adaptation reset */ + val = readl(base + PHY_ADPT_CFG0); + val |= PHY_ADPT_AFE_RST_OVRD_EN | PHY_ADPT_AFE_RST_OVRD_VAL; + writel(val, base + PHY_ADPT_CFG0); + + /* Override driver amplitude value to 900m */ + val = readl(base + PHY_RXEQ_TIME); + val |= PHY_RXEQ_TIME_OVRD_AMP_SOC; + val = u32_replace_bits(val, PHY_RXEQ_TIME_AMP_SOC_900M, PHY_RXEQ_TIME_CFG_AMP_SOC); + writel(val, base + PHY_RXEQ_TIME); + + /* Configure RX parameters */ + val = PHY_RX_REG0_RLOAD | + FIELD_PREP(PHY_RX_REG1_RTERM, 0x8) | + FIELD_PREP(PHY_RX_REG1_RC_CALI, 0x7) | + FIELD_PREP(PHY_RX_REG2_CSEL, 0x8) | + PHY_RX_REG2_FORCE_CSEL | + FIELD_PREP(PHY_RX_REG2_PSEL, 0x4) | + FIELD_PREP(PHY_RX_REG3_I_LOAD, 0x7) | + PHY_RX_REG3_SEL_CBOOST_CODE | + FIELD_PREP(PHY_RX_REG3_ADJ_BIAS, 0x1) | + FIELD_PREP(PHY_RX_REG3_RDEG1, 0x3); + writel(val, base + PHY_RX_REG_A); + + val = readl(base + PHY_RX_REG_B); + tmp = FIELD_PREP(PHY_RX_REG4_RDEG2, 0x2) | + PHY_RX_REG4_ENVOS | PHY_RX_REG4_RTERM_SEL | PHY_RX_REG4_MANUAL_CFG | + FIELD_PREP(PHY_RX_REG5_RCELL_VCM, 0x8) | + FIELD_PREP(PHY_RX_REG5_RCELL_BIAS, 0x8) | + FIELD_PREP(PHY_RX_REG6_H1_REG, 0x8) | + FIELD_PREP(PHY_RX_REG6_ADAPT_GAIN, 0x2); + val = u32_replace_bits(val, tmp, PHY_RX_REGB_MASK); + writel(val, base + PHY_RX_REG_B); + + /* + * Inform PHY that all PLL-related configuration is done. + * PLL will not start locking until PHY_CLK_SW_INIT_DONE is set. + */ + val = PHY_CLK_SW_INIT_DONE | PHY_CLK_PU_SSC_OUT | + FIELD_PREP(PHY_CLK_REFCLK_FREQ, PHY_CLK_REFCLK_24M) | + PHY_CLK_RXCLK_EN | PHY_CLK_TXCLK_EN | + PHY_CLK_PCLK_EN | PHY_CLK_PIPE_PCLK_EN; + writel(val, base + PHY_CLK_CFG); + + ret = readl_poll_timeout(base + PHY_CLK_CFG, val, + (val & PHY_CLK_PLL_READY), + PCIE_POLL_DELAY, PCIE_PLL_TIMEOUT); + if (ret) { + dev_err(&phy->dev, "PHY PLL polling timeout\n"); + return ret; + } + + return 0; +} + +static int k3_usb3phy_init(struct phy *phy) +{ + struct k3_lane_group *lg = phy_get_drvdata(phy); + int ret, i; + + for (i = 0; i < lg->data->lanes; i++) { + ret = k3_usb3phy_init_single(lg, lg->base + lg->data->offsets[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +const struct phy_ops k3_usb3_phy_ops = { + .init = k3_usb3phy_init, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL_GPL(k3_usb3_phy_ops); + +static int k3_pcie_phy_init(struct phy *phy) +{ + struct k3_lane_group *lg = phy_get_drvdata(phy); + void __iomem *phy_base = lg->base + lg->data->offsets[0]; + u32 val; + int ret; + int i; + + val = readl(phy_base + PHY_PLL_REG1); + val = u32_replace_bits(val, 0x2, GENMASK(15, 12)); + writel(val, phy_base + PHY_PLL_REG1); + + val = readl(phy_base + PHY_PLL_REG2); + val = u32_replace_bits(val, 0, BIT(21)); + writel(val, phy_base + PHY_PLL_REG2); + + for (i = 0; i < lg->data->lanes; i++) { + void __iomem *lane_base = lg->base + lg->data->offsets[i]; + + val = readl(lane_base + PCIE_RX_REG1); + val = u32_replace_bits(val, 0, 0x3); + writel(val, lane_base + PCIE_RX_REG1); + } + + val = readl(phy_base + PHY_PLL_REG2); + val |= BIT(20); + writel(val, phy_base + PHY_PLL_REG2); + + /* The write is needed as clock requires renegotiation */ + val = FIELD_PREP(PCIE_RX_REFCLK_MODE, PCIE_RX_REFCLK_MODE_DRIVER) | + PCIE_RX_SEL_TRI_CODE | + FIELD_PREP(PCIE_RX_LEGACY, PCIE_RX_LEGACY_DEFAULT); + writel(val, phy_base + PCIE_RX_REG1); + + /* pll_reg1 of lane0, disable SSC: pll[27:24] = 0 */ + val = readl(phy_base + PHY_PLL_REG1); + val = u32_replace_bits(val, 0, GENMASK(27, 24)); + writel(val, phy_base + PHY_PLL_REG1); + + for (i = 0; i < lg->data->lanes; i++) { + void __iomem *lane_base = lg->base + lg->data->offsets[i]; + + /* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */ + val = readl(lane_base + PHY_PU_SEL); + val = u32_replace_bits(val, 1, BIT(13)); + writel(val, lane_base + PHY_PU_SEL); + + /* disable en_sample_data_after_cdr_locked */ + val = readl(lane_base + PHY_RESET_CFG); + val = u32_replace_bits(val, 0, BIT(6)); + writel(val, lane_base + PHY_RESET_CFG); + + /* Dynamic Lock */ + val = readl(lane_base + PHY_MODE_CFG); + val = u32_replace_bits(val, 1, BIT(2)); + writel(val, lane_base + PHY_MODE_CFG); + + val = FIELD_PREP(PHY_RX_REG0_MASK, 0x10) | + FIELD_PREP(PHY_RX_REG1_MASK, 0x78) | + FIELD_PREP(PHY_RX_REG2_MASK, 0x98) | + FIELD_PREP(PHY_RX_REG3_MASK, 0xdf); + writel(val, lane_base + PHY_RX_REG_A); + + val = readl(lane_base + PHY_RX_REG_B); + val &= ~PHY_RX_REGB_MASK; + val |= FIELD_PREP(PHY_RX_REG4_MASK, 0xb4) | + FIELD_PREP(PHY_RX_REG5_MASK, 0x88) | + FIELD_PREP(PHY_RX_REG6_MASK, 0x28); + writel(val, lane_base + PHY_RX_REG_B); + + /* Set init done */ + val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG); + val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE); + writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG); + } + + ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val, + (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY, + PCIE_PLL_TIMEOUT); + if (ret) { + dev_err(&lg->phy->dev, "PHY PLL lock timeout\n"); + return ret; + } + + return 0; +} + +const struct phy_ops k3_pcie_phy_ops = { + .init = k3_pcie_phy_init, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL_GPL(k3_pcie_phy_ops); + +/* PHY rcal init requires APB_SPARE regmap access */ + +#define APB_SPARE_PU_CAL 0x178 +#define PU_CAL BIT(17) + +#define APB_SPARE_RCAL_HSIO 0x17c +#define APB_SPARE_PU_CAL_DONE BIT(8) +#define RCAL_OVRD_PTRIM GENMASK(23, 20) +#define RCAL_OVRD_NTRIM GENMASK(27, 24) +#define RCAL_OVRD_PTRIM_EN BIT(28) +#define RCAL_OVRD_NTRIM_EN BIT(29) +#define RCAL_OVRD_STABLE_VAL BIT(30) +#define RCAL_OVRD_STABLE_EN BIT(31) + +#define RCAL_OVRD_TRIM_EN (RCAL_OVRD_NTRIM_EN | RCAL_OVRD_PTRIM_EN) +#define RCAL_OVRD_TRIM_MASK (RCAL_OVRD_NTRIM | RCAL_OVRD_PTRIM) + +#define PU_CAL_TIMEOUT 2000000 + +static DEFINE_MUTEX(calibrate_lock); + +int k3_phy_calibrate(struct regmap *apb_spare) +{ + unsigned int val = 0; + int ret; + + guard(mutex)(&calibrate_lock); + + regmap_read(apb_spare, APB_SPARE_RCAL_HSIO, &val); + if (val & APB_SPARE_PU_CAL_DONE) + return 0; + + regmap_update_bits(apb_spare, APB_SPARE_PU_CAL, PU_CAL, + PU_CAL); + + ret = regmap_read_poll_timeout(apb_spare, APB_SPARE_RCAL_HSIO, + val, (val & APB_SPARE_PU_CAL_DONE), PCIE_POLL_DELAY, + PU_CAL_TIMEOUT); + + if (ret) + regmap_update_bits(apb_spare, APB_SPARE_RCAL_HSIO, + RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL | + RCAL_OVRD_TRIM_MASK | RCAL_OVRD_STABLE_EN, + RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL | + FIELD_PREP(RCAL_OVRD_NTRIM, 0x6) | + FIELD_PREP(RCAL_OVRD_PTRIM, 0xa) | + RCAL_OVRD_STABLE_EN); + + return 0; +} +EXPORT_SYMBOL_GPL(k3_phy_calibrate); + +MODULE_DESCRIPTION("SpacemiT K3 PHY common ops"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/spacemit/phy-k3-common.h b/drivers/phy/spacemit/phy-k3-common.h new file mode 100644 index 00000000000000..49009c3c313a29 --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-common.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _PHY_K3_COMMON_H +#define _PHY_K3_COMMON_H + +#include + +struct k3_phy_lane_group_data { + u32 lanes; + u8 config; + u8 mask; + u32 offsets[] __counted_by(lanes); +}; + +struct k3_lane_group { + const struct k3_phy_lane_group_data *data; + void __iomem *base; + struct phy *phy; + bool is_pcie; +}; + +extern const struct phy_ops k3_pcie_phy_ops; +extern const struct phy_ops k3_usb3_phy_ops; + +int k3_phy_calibrate(struct regmap *apb_spare); + +#endif From 6e226b3a87754a7d58d356d3cae93042799cd630 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:22:30 +0800 Subject: [PATCH 261/521] FROMLIST: gpio: sifive: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260704122230.75964-1-pengpeng@iscas.ac.cn Signed-off-by: Han Gao --- drivers/gpio/gpio-sifive.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index 94ef2efbd14f57..e9a992cd4b9cdf 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -260,6 +260,7 @@ static const struct of_device_id sifive_gpio_match[] = { { .compatible = "sifive,fu540-c000-gpio" }, { }, }; +MODULE_DEVICE_TABLE(of, sifive_gpio_match); static struct platform_driver sifive_gpio_driver = { .probe = sifive_gpio_probe, From 68efb41cd660f2688179571244f572c1d3977660 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:25:38 +0800 Subject: [PATCH 262/521] FROMLIST: PCI: dwc: spacemit: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260704122538.92830-1-pengpeng@iscas.ac.cn Signed-off-by: Han Gao --- drivers/pci/controller/dwc/pcie-spacemit-k1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index 680acc93f53957..7502d66bd0d0a8 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -547,6 +547,7 @@ static const struct of_device_id k1_pcie_of_match_table[] = { { .compatible = "spacemit,k3-pcie", .data = &k3_pcie_device_data}, { } }; +MODULE_DEVICE_TABLE(of, k1_pcie_of_match_table); static struct platform_driver k1_pcie_driver = { .probe = k1_pcie_probe, From 41fb0cf42a672069c043fc69a5b53a911542fd04 Mon Sep 17 00:00:00 2001 From: Anmeng Zhang Date: Wed, 8 Jul 2026 09:50:46 +0800 Subject: [PATCH 263/521] FROMLIST: riscv: stacktrace: Fix NULL task pointer in ftrace_graph_ret_addr() When dump_backtrace() is called with a NULL task argument, and the call stack contains functions that are being traced, walk_stackframe() eventually calls ftrace_graph_ret_addr() with the NULL task pointer, which results in a NULL pointer dereference when accessing task->curr_ret_stack. Fix this by assigning the task pointer to the current task in walk_stackframe() when it is NULL, keeping the behavior consistent with other architectures. Fixes: ecbf894165a2 ("riscv: Fix ftrace_graph_ret_addr() to use the correct task pointer") Reviewed-by: Shengming Hu Reviewed-by: Rui Gao Reviewed-by: Luo Haiyang Reviewed-by: Run Zhang Signed-off-by: Anmeng Zhang Link: https://lore.kernel.org/r/20260708095046716mVQlEvhyoXko9eueCxict@zte.com.cn Signed-off-by: Han Gao --- arch/riscv/kernel/stacktrace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index b41b6255751cb1..694c9c7fde4a44 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -52,11 +52,14 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, int graph_idx = 0; int level = 0; + if (task == NULL) + task = current; + if (regs) { fp = frame_pointer(regs); sp = user_stack_pointer(regs); pc = instruction_pointer(regs); - } else if (task == NULL || task == current) { + } else if (task == current) { fp = (unsigned long)__builtin_frame_address(0); sp = current_stack_pointer; pc = (unsigned long)walk_stackframe; From 2e5bf33a90dca8691de4a03fa04aa4799943c166 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Mon, 6 Jul 2026 20:35:54 +0800 Subject: [PATCH 264/521] FROMLIST: riscv: ftrace: only use pre-function NOPs with call ops Commit c217157bcd1d ("riscv: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS") changed CC_FLAGS_FTRACE to use -fpatchable-function-entry=8,4 or -fpatchable-function-entry=4,2 for all dynamic ftrace builds. That layout makes the compiler place an 8-byte area before the function entry, which is used as the per-callsite ftrace_ops literal when CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS is enabled. RISC-V can still build with CONFIG_DYNAMIC_FTRACE=y and CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=n, for example when CONFIG_CFI_CLANG is enabled because HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS is selected only when !CFI_CLANG. In that configuration ftrace_call_adjust() does not skip the pre-function literal area and only returns addr + MCOUNT_AUIPC_SIZE. With the pre-function layout, that points into the pre-entry padding instead of the callsite jalr, so dynamic ftrace records the wrong patch address. Use the pre-function literal layout only when call ops are enabled. Otherwise keep the previous patchable-function-entry counts so the recorded address matches ftrace_call_adjust() non-call-ops path. Fixes: c217157bcd1d ("riscv: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS") Cc: stable@vger.kernel.org Signed-off-by: Rui Qi Link: https://lore.kernel.org/r/20260706123554.455065-1-qirui.001@bytedance.com Signed-off-by: Han Gao --- arch/riscv/Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index ce0cc737f87096..afc418219c71df 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -14,11 +14,19 @@ endif ifeq ($(CONFIG_DYNAMIC_FTRACE),y) LDFLAGS_vmlinux += --no-relax KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY +ifeq ($(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS),y) ifeq ($(CONFIG_RISCV_ISA_C),y) CC_FLAGS_FTRACE := -fpatchable-function-entry=8,4 else CC_FLAGS_FTRACE := -fpatchable-function-entry=4,2 endif +else +ifeq ($(CONFIG_RISCV_ISA_C),y) + CC_FLAGS_FTRACE := -fpatchable-function-entry=4 +else + CC_FLAGS_FTRACE := -fpatchable-function-entry=2 +endif +endif endif ifeq ($(CONFIG_CMODEL_MEDLOW),y) From b4399e84e162520bb3096f2bcfc781d492fe58e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 9 Jul 2026 08:49:00 +0200 Subject: [PATCH 265/521] FROMLIST: riscv: vdso: Only try to install vDSO when present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vdso.so.dbg is only built with CONFIG_MMU. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607090258.iSAUYlO1-lkp@intel.com/ Fixes: f157d411a9eb ("riscv: add missing vdso_install target") Fixes: 3edf39916977 ("vDSO, kbuild: Provide vDSO debug variants at runtime") Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20260709-riscv-install-vdso-v1-1-0ba4345419ca@linutronix.de Signed-off-by: Han Gao --- arch/riscv/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index afc418219c71df..729a46ac2b0d7d 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -176,7 +176,7 @@ vdso_prepare: prepare0 endif endif -vdso-install-y += arch/riscv/kernel/vdso/vdso.so.dbg +vdso-install-$(CONFIG_MMU) += arch/riscv/kernel/vdso/vdso.so.dbg vdso-install-$(CONFIG_RISCV_USER_CFI) += arch/riscv/kernel/vdso_cfi/vdso-cfi.so.dbg vdso-install-$(CONFIG_COMPAT) += arch/riscv/kernel/compat_vdso/compat_vdso.so.dbg From 9270a736316a432e0fc08bb1adbced6aa1300910 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:04:13 +0800 Subject: [PATCH 266/521] FROMLIST: riscv: dts: spacemit: k3: add USB controller and USB phy support Add all USB device node to the Spacemit K3. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260709040415.977784-2-inochiama@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 13 ++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 42 ++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index c7ecb2f5cb4d7b..c418cd4fb7d6bc 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -220,6 +220,11 @@ }; }; +&combophy { + spacemit,apmu = <&syscon_apmu 0x11>; + status = "okay"; +}; + ð0 { pinctrl-names = "default"; pinctrl-0 = <&gmac0_rgmii_0_cfg>, <&gmac0_phy_0_cfg>; @@ -318,6 +323,14 @@ status = "okay"; }; +&usb3d_u2phy { + status = "okay"; +}; + +&usb3d { + status = "okay"; +}; + &ufshc { status = "okay"; }; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 862e5698972e76..ef75a70c94a03a 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include /dts-v1/; @@ -438,6 +439,47 @@ dma-noncoherent; ranges; + usb3d: usb@81a00000 { + compatible = "spacemit,k3-dwc3"; + reg = <0x0 0x81a00000 0x0 0x10000>; + interrupts = <149 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_apmu CLK_APMU_USB3_PORTD_BUS>; + clock-names = "usbdrd30"; + resets = <&syscon_apmu RESET_APMU_USB3_D_AHB>, + <&syscon_apmu RESET_APMU_USB3_D_VCC>, + <&syscon_apmu RESET_APMU_USB3_D_PHY>; + reset-names = "ahb", "vcc", "phy"; + phys = <&usb3d_u2phy>, + <&combophy 4 PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + phy_type = "utmi"; + snps,dis_enblslpm_quirk; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; + snps,parkmode-disable-ss-quirk; + dr_mode = "host"; + status = "disabled"; + }; + + usb3d_u2phy: phy@81b00000 { + compatible = "spacemit,k3-usb2-phy"; + reg = <0x0 0x81b00000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_USB2_BUS>; + #phy-cells = <0>; + status = "disabled"; + }; + + combophy: phy@81d00000 { + compatible = "spacemit,k3-combo-phy"; + reg = <0x0 0x81d00000 0x0 0x600000>; + #phy-cells = <2>; + spacemit,apb-spare = <&pll>; + status = "disabled"; + }; + usb2_host: usb@c0a00000 { compatible = "spacemit,k3-dwc3"; reg = <0x0 0xc0a00000 0x0 0x10000>; From 4a3e9e8f8221d6a69999bc742b3082595ced8da7 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 9 Jul 2026 12:04:14 +0800 Subject: [PATCH 267/521] FROMLIST: riscv: dts: spacemit: k3: Add PCIe device node Add all PCIe device node for Spacemit K3. Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260709040415.977784-3-inochiama@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 38 ++++ arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 33 ++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 195 +++++++++++++++++++ 3 files changed, 266 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index c418cd4fb7d6bc..cb51126bf4e4db 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -301,6 +301,44 @@ }; }; +&pcie0_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_0_cfg>; + num-lanes = <4>; + status = "okay"; + + pcie@0 { + vpcie3v3-supply = <®_aux_vcc3v3>; + phys = <&combophy 0 PHY_TYPE_PCIE>, + <&combophy 1 PHY_TYPE_PCIE>; + }; +}; + +&pcie2_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_0_cfg>; + num-lanes = <2>; + status = "okay"; + + pcie@0 { + vpcie3v3-supply = <®_aux_vcc3v3>; + phys = <&combophy 2 PHY_TYPE_PCIE>, + <&combophy 3 PHY_TYPE_PCIE>; + }; +}; + +&pcie4_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie4_0_cfg>; + num-lanes = <1>; + status = "okay"; + + pcie@0 { + vpcie3v3-supply = <®_aux_vcc3v3>; + phys = <&combophy 5 PHY_TYPE_PCIE>; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_0_cfg>; diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi index 5b9045e014345d..746eb2ee179910 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -722,6 +722,39 @@ }; }; + pcie0_0_cfg: pcie0-0-cfg { + pcie0-0-pins { + pinmux = , /* pcie0 perst */ + ; /* pcie0 clkreq */ + + bias-pull-up = <1>; + drive-strength = <33>; + power-source = <1800>; + }; + }; + + pcie2_0_cfg: pcie2-0-cfg { + pcie2-0-pins { + pinmux = , /* pcie2 perst */ + ; /* pcie2 clkreq */ + + drive-strength = <38>; + power-source = <3300>; + }; + }; + + pcie4_0_cfg: pcie4-0-cfg { + pcie4-0-pins { + pinmux = , /* pcie4 perst */ + , /* pcie4 wake */ + ; /* pcie4 clkreq */ + + bias-pull-up = <1>; + drive-strength = <33>; + power-source = <1800>; + }; + }; + mmc1_cfg: mmc1-cfg { mmc1-data-cmd-pins { pinmux = , /* mmc1 dat3 */ diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index ef75a70c94a03a..d7beabc88d866a 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -439,6 +439,201 @@ dma-noncoherent; ranges; + pcie0_rc: pcie@80000000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80000000 0x0 0x00001000>, + <0x0 0x80300000 0x0 0x00003f20>, + <0x11 0x00000000 0x0 0x00010000>, + <0x0 0x82900000 0x0 0x00001000>, + <0x0 0x80100000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "link", "dbi2"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTA_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTA_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTA_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x00 0x00010000 0x11 0x00010000 0x0 0x00100000>, + <0x02000000 0x0 0x00110000 0x11 0x00110000 0x0 0x7fef0000>, + <0x43000000 0x18 0x00000000 0x18 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_A_DBI>, + <&syscon_apmu RESET_APMU_PCIE_A_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_A_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + max-link-speed = <3>; + linux,pci-domain = <0>; + spacemit,apmu = <&syscon_apmu 0x1f0>; + status = "disabled"; + + pcie0_port: pcie@0 { + device_type = "pci"; + compatible = "pciclass,0604"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie1_rc: pcie@80400000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80400000 0x0 0x00001000>, + <0x0 0x80700000 0x0 0x00003f20>, + <0x11 0x80000000 0x0 0x00010000>, + <0x0 0x82c00000 0x0 0x00001000>, + <0x0 0x80500000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "link", "dbi2"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTB_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTB_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTB_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x0 0x00010000 0x11 0x80010000 0x0 0x00100000>, + <0x02000000 0x0 0x80110000 0x11 0x80110000 0x0 0x7fef0000>, + <0x43000000 0x16 0x00000000 0x16 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_B_DBI>, + <&syscon_apmu RESET_APMU_PCIE_B_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_B_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + max-link-speed = <3>; + linux,pci-domain = <1>; + spacemit,apmu = <&syscon_apmu 0x1d0>; + status = "disabled"; + + pcie1_port: pcie@0 { + device_type = "pci"; + compatible = "pciclass,0604"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie2_rc: pcie@80800000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80800000 0x0 0x00001000>, + <0x0 0x80b00000 0x0 0x00003f20>, + <0x12 0x00000000 0x0 0x00010000>, + <0x0 0x82d00000 0x0 0x00001000>, + <0x0 0x80900000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "link", "dbi2"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTC_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTC_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTC_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x00 0x00000000 0x12 0x00010000 0x0 0x00100000>, + <0x02000000 0x0 0x00110000 0x12 0x00110000 0x0 0x7fef0000>, + <0x43000000 0x15 0x00000000 0x15 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_C_DBI>, + <&syscon_apmu RESET_APMU_PCIE_C_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_C_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + linux,pci-domain = <2>; + max-link-speed = <3>; + spacemit,apmu = <&syscon_apmu 0x1c8>; + status = "disabled"; + + pcie2_port: pcie@0 { + device_type = "pci"; + compatible = "pciclass,0604"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie3_rc: pcie@80c00000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80c00000 0x0 0x00001000>, + <0x0 0x80f00000 0x0 0x00003f20>, + <0x12 0x80000000 0x0 0x00010000>, + <0x0 0x82a00000 0x0 0x00001000>, + <0x0 0x80d00000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "link", "dbi2"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTD_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTD_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTD_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x0 0x00010000 0x12 0x80010000 0x0 0x100000>, + <0x02000000 0x0 0x80110000 0x12 0x80110000 0x0 0x3fef0000>, + <0x43000000 0x14 0x00000000 0x14 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_D_DBI>, + <&syscon_apmu RESET_APMU_PCIE_D_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_D_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + linux,pci-domain = <3>; + bus-range = <0x00 0xff>; + max-link-speed = <3>; + spacemit,apmu = <&syscon_apmu 0x1e0>; + status = "disabled"; + + pcie3_port: pcie@0 { + device_type = "pci"; + compatible = "pciclass,0604"; + reg = <0x0 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie4_rc: pcie@81000000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x81000000 0x0 0x00001000>, + <0x0 0x81300000 0x0 0x00003f20>, + <0x12 0xc0000000 0x0 0x00010000>, + <0x0 0x82b00000 0x0 0x00001000>, + <0x0 0x81100000 0x0 0x00001000>; + reg-names = "dbi", "atu", "config", "link", "dbi2"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTE_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTE_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTE_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x0 0x00000000 0x12 0xc0010000 0x0 0x100000>, + <0x02000000 0x0 0xc0110000 0x12 0xc0110000 0x0 0x3fef0000>, + <0x43000000 0x13 0x00000000 0x13 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_E_DBI>, + <&syscon_apmu RESET_APMU_PCIE_E_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_E_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + linux,pci-domain = <4>; + max-link-speed = <3>; + spacemit,apmu = <&syscon_apmu 0x1e8>; + status = "disabled"; + + pcie4_port: pcie@0 { + device_type = "pci"; + compatible = "pciclass,0604"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + usb3d: usb@81a00000 { compatible = "spacemit,k3-dwc3"; reg = <0x0 0x81a00000 0x0 0x10000>; From 4931c06dba59666ea0748241e45bbe83fc7908a8 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 10 Jul 2026 14:33:12 +0800 Subject: [PATCH 268/521] FROMLIST: riscv: dts: spacemit: Add enough deassert time for the PHY on PICO ITX RTL8211F require at least 50ms deassert to guarantee the register access, 10ms is only enough for the PHY reset. Fixes: 74657a376960 ("riscv: dts: spacemit: Add ethernet device for K3") Signed-off-by: Inochi Amaoto Tested-by: E Shattow Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260710063314.1030249-1-inochiama@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index cb51126bf4e4db..c412f70020116b 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -238,7 +238,7 @@ reg = <1>; reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; - reset-deassert-us = <10000>; + reset-deassert-us = <50000>; realtek,aldps-enable; realtek,clkout-disable; }; From 066103b099183ab309c410b54cb92523d30e1943 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 10 Jul 2026 14:33:13 +0800 Subject: [PATCH 269/521] FROMLIST: riscv: dts: spacemit: Add enough deassert time for the PHY on com260 board RTL8211F require at least 50ms deassert to guarantee the register access, 10ms is only enough for the PHY reset. Fixes: cfe5c91cb73c ("riscv: dts: spacemit: k3: Initial support for CoM260-IFX board") Signed-off-by: Inochi Amaoto Link: https://lore.kernel.org/r/20260710063314.1030249-2-inochiama@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-com260.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k3-com260.dtsi b/arch/riscv/boot/dts/spacemit/k3-com260.dtsi index a38d7b738258de..b704b537385c7c 100644 --- a/arch/riscv/boot/dts/spacemit/k3-com260.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-com260.dtsi @@ -178,7 +178,7 @@ reg = <1>; reset-gpios = <&gpio 1 5 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; - reset-deassert-us = <10000>; + reset-deassert-us = <50000>; }; }; }; From cca3b88a4a4f63142f00522d2629fb1b629597a2 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sun, 12 Jul 2026 12:52:33 +0800 Subject: [PATCH 270/521] FROMLIST: Failed to reinit phy of spacemit-dwmac when reset-gpio is present TL;DR: The DWMAC on Spacemit K3 is failled to register phy after the reload the driver module (rmmod then insmod). Because the reset-gpio is asserted while unloading the driver and is not desserted before reading the c22 id, which leads to a fault. Description In a few days ago, E Shattow reports he has sufferred the a weird issue, when unloading the spacemit-dwmac driver and then reloading it, he got the following error: [ 60.713071] mdio_bus stmmac-0: MDIO device at address 1 is missing. This only occurs when reloading the driver but the first initialization successed. After some function tracking, I found it is failed at reading c22 id. The call graph is as the follows: stmmac_mdio_register mdiobus_alloc of_mdiobus_register __of_mdiobus_register __mdiobus_register __of_mdiobus_parse_phys of_mdiobus_child_is_phy of_mdiobus_register_phy fwnode_mdiobus_register_phy get_phy_device get_phy_c22_id By checking the difference between the initialization process and the reloading process, I found the reset gpio is asserted in the function mdiobus_unregister(). And there is no any function desserted this reset gpio in the loading stage. And in the initialization process, the reset goio is deasserted. This bug report is sent as I found it is hard to solve this problem and ask for help to fix this issue as it is related to the framework instead of a specific driver. The possible workaround I found is as the following, just use the phy id in the compatible string (Confirmed by dlan): Tested-by: E Shattow Link: https://lore.kernel.org/r/20260712045233.800748-1-inochiama@gmail.com Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index c412f70020116b..7327eb65593c63 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -234,7 +234,8 @@ mdio { phy0: phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id001c.c916", + "ethernet-phy-ieee802.3-c22"; reg = <1>; reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; From c5e8e9c3f8f636a40dcc67d7cdd16c9e442caa4a Mon Sep 17 00:00:00 2001 From: Eder Zulian Date: Tue, 14 Jul 2026 20:56:48 +0200 Subject: [PATCH 271/521] FROMLIST: riscv: mm: Move dma_contiguous_reserve() after NUMA initialization Move the dma_contiguous_reserve() call from setup_bootmem() to misc_mem_init(), placing it after arch_numa_init(). This ensures that NUMA topology is initialized when reserving contiguous memory for DMA. Tested with CMA_SIZE_PERNUMA enabled and two NUMA nodes on QEMU: qemu-system-riscv64 \ -machine virt \ -nographic \ -smp 2 -m 512M \ -numa node,nodeid=0,cpus=0,memdev=m0 \ -numa node,nodeid=1,cpus=1,memdev=m1 \ -object memory-backend-ram,id=m0,size=256M \ -object memory-backend-ram,id=m1,size=256M \ -kernel arch/riscv/boot/Image \ -append "console=ttyS0 earlycon loglevel=7 cma=16M" Unpatched kernel's log (one global CMA pool and no per-nodepools, 16 MiB cma-reserved): [ 0.000000] cma: Reserved 16 MiB at 0x000000009ee00000 ... [ 0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x000000008fffffff] [ 0.000000] Initmem setup node 1 [mem 0x0000000090000000-0x000000009fffffff] ... [ 0.056397] smp: Brought up 2 nodes, 2 CPUs [ 0.066135] Memory: 449288K/524288K available (12279K kernel code, 5980K rwdata, 6144K rodata, 2467K init, 482K bss, 54504K reserved, 16384K cma-reserved) Patched kernel's log (three CMA pools are created, 48 MiB total), [ 0.000000] cma: Reserved 16 MiB at 0x000000009ee00000 [ 0.000000] cma: Reserved 16 MiB at 0x000000008ee00000 [ 0.000000] cma: Reserved 16 MiB at 0x000000009de00000 ... [ 0.054251] smp: Brought up 2 nodes, 2 CPUs [ 0.064082] Memory: 416520K/524288K available (12279K kernel code, 5980K rwdata, 6144K rodata, 2467K init, 482K bss, 54504K reserved, 49152K cma-reserved) Signed-off-by: Eder Zulian Link: https://lore.kernel.org/r/20260714185648.1082483-1-ezulian@redhat.com Signed-off-by: Han Gao --- arch/riscv/mm/init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 2ec9a9df20cb87..86b0527d993e2a 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -303,8 +303,6 @@ static void __init setup_bootmem(void) */ if (!IS_ENABLED(CONFIG_BUILTIN_DTB)) memblock_reserve(dtb_early_pa, fdt_totalsize(dtb_early_va)); - - dma_contiguous_reserve(dma32_phys_limit); } #ifdef CONFIG_RELOCATABLE @@ -1363,6 +1361,7 @@ void __init misc_mem_init(void) { early_memtest(min_low_pfn << PAGE_SHIFT, max_low_pfn << PAGE_SHIFT); arch_numa_init(); + dma_contiguous_reserve(dma32_phys_limit); #ifdef CONFIG_SPARSEMEM_VMEMMAP /* The entire VMEMMAP region has been populated. Flush TLB for this region */ local_flush_tlb_kernel_range(VMEMMAP_START, VMEMMAP_END); From a2655b552d16fcf033d49ea9c7c1d6d81d34ea95 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 17 Jul 2026 00:26:01 -0700 Subject: [PATCH 272/521] FROMLIST: dt-bindings: soc: spacemit: k3: add i2s_sysclk, i2s_bclk_factor and i2s1_sysclk_src IDs Add three new clock IDs to expose clocks introduced by the topology fix: - CLK_MPMU_I2S_SYSCLK (51): the common i2s sysclk DDN at MPMU_ISCCR1 - CLK_MPMU_I2S_BCLK_FACTOR (52): the implicit /2 factor feeding i2s_bclk - CLK_MPMU_I2S1_SYSCLK_SRC (53): the dedicated i2s1 sysclk source mux CLK_MPMU_I2S1_SYSCLK keeps its existing ID (47) but will be repointed to the real per-instance i2s1 clock in a subsequent patch. No in-tree user references this ID so the semantic change is contained. Fixes: efe897b557e2 ("dt-bindings: soc: spacemit: k3: add clock support") Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260717-k3-clk-fix-i2s-v1-1-e95001a692ee@linux.spacemit.com Signed-off-by: Han Gao --- include/dt-bindings/clock/spacemit,k3-clocks.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/dt-bindings/clock/spacemit,k3-clocks.h b/include/dt-bindings/clock/spacemit,k3-clocks.h index 69978ab0b29f86..4f1099f5da1c4b 100644 --- a/include/dt-bindings/clock/spacemit,k3-clocks.h +++ b/include/dt-bindings/clock/spacemit,k3-clocks.h @@ -145,6 +145,9 @@ #define CLK_MPMU_I2S3_SYSCLK 48 #define CLK_MPMU_I2S4_SYSCLK 49 #define CLK_MPMU_I2S5_SYSCLK 50 +#define CLK_MPMU_I2S_SYSCLK 51 +#define CLK_MPMU_I2S_BCLK_FACTOR 52 +#define CLK_MPMU_I2S1_SYSCLK_SRC 53 /* APBC clocks */ #define CLK_APBC_UART0 0 From 0379311ae32776d69ac9b83ea49143ec046f13a2 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 17 Jul 2026 00:26:02 -0700 Subject: [PATCH 273/521] FROMLIST: clk: spacemit: k3: fix i2s clock topology The K3 i2s clocks were modelled as a single path behind one MPMU register: pll1_d96_25p6 / i2s_153p6_base `-- i2s_sysclk_src (mux+gate, MPMU_ISCCR) `-- i2s1_sysclk (DDN, MPMU_ISCCR) |-- i2s_bclk (div+gate, MPMU_ISCCR) `-- i2s2_sysclk (mux, parent 0) The hardware actually has two i2s clock control registers, ISCCR0 (0x0040) and ISCCR1 (0x0044): ISCCR1 drives the common sysclk shared by i2s0/2/3/4/5 and the common bclk, whose divider always implies a fixed 1/2 factor in front, while ISCCR0 drives a dedicated path for i2s1: pll1_d96_25p6 / i2s_153p6_base |-- i2s_sysclk_src (mux+gate, MPMU_ISCCR1) | `-- i2s_sysclk (DDN, MPMU_ISCCR1) | |-- i2s_bclk_factor (fixed factor, /2) | | `-- i2s_bclk (div+gate, MPMU_ISCCR1) | `-- i2s2_sysclk (mux, parent 0) `-- i2s1_sysclk_src (mux+gate, MPMU_ISCCR0) `-- i2s1_sysclk (DDN, MPMU_ISCCR0) Because of this mismatch, i2s_bclk reported twice the real rate, and the dedicated i2s1 clock path could not be described in DT at all. Model the tree as above: split the MPMU_ISCCR register macro into MPMU_ISCCR0 and MPMU_ISCCR1 to match the hardware register names, rename the common DDN to i2s_sysclk, insert the fixed 1/2 factor i2s_bclk_factor in front of i2s_bclk, and add the i2s1_sysclk_src mux and i2s1_sysclk DDN backed by MPMU_ISCCR0. CLK_MPMU_I2S1_SYSCLK now refers to the dedicated i2s1 clock; no in-tree user references this ID, so nothing is affected by the change of meaning. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260717-k3-clk-fix-i2s-v1-2-e95001a692ee@linux.spacemit.com Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 21 ++++++++++++++++----- include/soc/spacemit/k3-syscon.h | 3 ++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 5d0e31f07d6d2e..5cb14349358a75 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -218,11 +218,19 @@ static const struct clk_parent_data i2s_sysclk_src_parents[] = { CCU_PARENT_HW(pll1_d96_25p6), CCU_PARENT_HW(i2s_153p6_base), }; -CCU_MUX_GATE_DEFINE(i2s_sysclk_src, i2s_sysclk_src_parents, MPMU_ISCCR, 30, 1, BIT(31), 0); +CCU_MUX_GATE_DEFINE(i2s_sysclk_src, i2s_sysclk_src_parents, MPMU_ISCCR1, 30, 1, BIT(31), 0); -CCU_DDN_DEFINE(i2s1_sysclk, i2s_sysclk_src, MPMU_ISCCR, 0, 15, 15, 12, 1, 0); +CCU_DDN_DEFINE(i2s_sysclk, i2s_sysclk_src, MPMU_ISCCR1, 0, 15, 15, 12, 1, 0); -CCU_DIV_GATE_DEFINE(i2s_bclk, CCU_PARENT_HW(i2s1_sysclk), MPMU_ISCCR, 27, 2, BIT(29), 0); +CCU_FACTOR_DEFINE(i2s_bclk_factor, CCU_PARENT_HW(i2s_sysclk), 2, 1); +/* + * Divider of i2s_bclk always implies a 1/2 factor, which is + * described by i2s_bclk_factor. + */ +CCU_DIV_GATE_DEFINE(i2s_bclk, CCU_PARENT_HW(i2s_bclk_factor), MPMU_ISCCR1, 27, 2, BIT(29), 0); + +CCU_MUX_GATE_DEFINE(i2s1_sysclk_src, i2s_sysclk_src_parents, MPMU_ISCCR0, 30, 1, BIT(31), 0); +CCU_DDN_DEFINE(i2s1_sysclk, i2s1_sysclk_src, MPMU_ISCCR0, 0, 15, 15, 12, 1, 0); static const struct clk_parent_data i2s_sysclk_parents[] = { CCU_PARENT_HW(pll1_d4_614p4), @@ -243,7 +251,7 @@ CCU_DDN_DEFINE(i2s4_sysclk_div, i2s4_sysclk_sel, MPMU_I2S4_SYSCLK, 0, 16, 16, 16 CCU_DDN_DEFINE(i2s5_sysclk_div, i2s5_sysclk_sel, MPMU_I2S5_SYSCLK, 0, 16, 16, 16, 1, 0); static const struct clk_parent_data i2s2_sysclk_parents[] = { - CCU_PARENT_HW(i2s1_sysclk), + CCU_PARENT_HW(i2s_sysclk), CCU_PARENT_HW(i2s2_sysclk_div), }; CCU_GATE_DEFINE(i2s0_sysclk, CCU_PARENT_HW(i2s0_sysclk_div), MPMU_I2S_SYSCLK_CTRL, BIT(2), 0); @@ -1162,8 +1170,11 @@ static struct clk_hw *k3_ccu_mpmu_hws[] = { [CLK_MPMU_I2S_153P6] = &i2s_153p6.common.hw, [CLK_MPMU_I2S_153P6_BASE] = &i2s_153p6_base.common.hw, [CLK_MPMU_I2S_SYSCLK_SRC] = &i2s_sysclk_src.common.hw, - [CLK_MPMU_I2S1_SYSCLK] = &i2s1_sysclk.common.hw, + [CLK_MPMU_I2S_SYSCLK] = &i2s_sysclk.common.hw, + [CLK_MPMU_I2S_BCLK_FACTOR] = &i2s_bclk_factor.common.hw, [CLK_MPMU_I2S_BCLK] = &i2s_bclk.common.hw, + [CLK_MPMU_I2S1_SYSCLK_SRC] = &i2s1_sysclk_src.common.hw, + [CLK_MPMU_I2S1_SYSCLK] = &i2s1_sysclk.common.hw, [CLK_MPMU_I2S0_SYSCLK_SEL] = &i2s0_sysclk_sel.common.hw, [CLK_MPMU_I2S2_SYSCLK_SEL] = &i2s2_sysclk_sel.common.hw, [CLK_MPMU_I2S3_SYSCLK_SEL] = &i2s3_sysclk_sel.common.hw, diff --git a/include/soc/spacemit/k3-syscon.h b/include/soc/spacemit/k3-syscon.h index a68255dd641f31..959d8d0786fe03 100644 --- a/include/soc/spacemit/k3-syscon.h +++ b/include/soc/spacemit/k3-syscon.h @@ -45,7 +45,8 @@ #define POSR_PLL7_LOCK BIT(30) #define POSR_PLL8_LOCK BIT(31) #define MPMU_SUCCR 0x0014 -#define MPMU_ISCCR 0x0044 +#define MPMU_ISCCR0 0x0040 +#define MPMU_ISCCR1 0x0044 #define MPMU_WDTPCR 0x0200 #define MPMU_RIPCCR 0x0210 #define MPMU_ACGR 0x1024 From e2702f509f91187e239c97677f8888bacd08b1f4 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Fri, 17 Jul 2026 00:26:03 -0700 Subject: [PATCH 274/521] FROMLIST: clk: spacemit: k3: fix missing /2 factor in i2s sysclk dividers The i2s{0,2,3,4,5}_sysclk_div DDNs have an additional fixed 1/2 divider in the hardware IP after the configurable divider, so the real output rate is: rate = parent_rate * num / (den * 2) Set pre_div to 2 to account for it. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20260717-k3-clk-fix-i2s-v1-3-e95001a692ee@linux.spacemit.com Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 5cb14349358a75..972499084d3adf 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -244,11 +244,11 @@ CCU_MUX_DEFINE(i2s3_sysclk_sel, i2s_sysclk_parents, MPMU_I2S_SYSCLK_CTRL, 12, 2, CCU_MUX_DEFINE(i2s4_sysclk_sel, i2s_sysclk_parents, MPMU_I2S_SYSCLK_CTRL, 16, 2, 0); CCU_MUX_DEFINE(i2s5_sysclk_sel, i2s_sysclk_parents, MPMU_I2S_SYSCLK_CTRL, 20, 2, 0); -CCU_DDN_DEFINE(i2s0_sysclk_div, i2s0_sysclk_sel, MPMU_I2S0_SYSCLK, 0, 16, 16, 16, 1, 0); -CCU_DDN_DEFINE(i2s2_sysclk_div, i2s2_sysclk_sel, MPMU_I2S2_SYSCLK, 0, 16, 16, 16, 1, 0); -CCU_DDN_DEFINE(i2s3_sysclk_div, i2s3_sysclk_sel, MPMU_I2S3_SYSCLK, 0, 16, 16, 16, 1, 0); -CCU_DDN_DEFINE(i2s4_sysclk_div, i2s4_sysclk_sel, MPMU_I2S4_SYSCLK, 0, 16, 16, 16, 1, 0); -CCU_DDN_DEFINE(i2s5_sysclk_div, i2s5_sysclk_sel, MPMU_I2S5_SYSCLK, 0, 16, 16, 16, 1, 0); +CCU_DDN_DEFINE(i2s0_sysclk_div, i2s0_sysclk_sel, MPMU_I2S0_SYSCLK, 0, 16, 16, 16, 2, 0); +CCU_DDN_DEFINE(i2s2_sysclk_div, i2s2_sysclk_sel, MPMU_I2S2_SYSCLK, 0, 16, 16, 16, 2, 0); +CCU_DDN_DEFINE(i2s3_sysclk_div, i2s3_sysclk_sel, MPMU_I2S3_SYSCLK, 0, 16, 16, 16, 2, 0); +CCU_DDN_DEFINE(i2s4_sysclk_div, i2s4_sysclk_sel, MPMU_I2S4_SYSCLK, 0, 16, 16, 16, 2, 0); +CCU_DDN_DEFINE(i2s5_sysclk_div, i2s5_sysclk_sel, MPMU_I2S5_SYSCLK, 0, 16, 16, 16, 2, 0); static const struct clk_parent_data i2s2_sysclk_parents[] = { CCU_PARENT_HW(i2s_sysclk), From b47524a7f7ae1875eed33a3ec5935e1d7c73931b Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Thu, 16 Jul 2026 23:19:01 +0200 Subject: [PATCH 275/521] FROMLIST: riscv: defconfig: enable RFKILL and RFKILL_GPIO The SpacemiT K3 Pico-ITX requires RFKILL_GPIO to enable Bluetooth and WLAN. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260716213314.3027969-2-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/configs/defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index c2c37327b9877f..d66b8f27d6e83d 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -98,6 +98,8 @@ CONFIG_NET_CLS_CGROUP=m CONFIG_NETLINK_DIAG=y CONFIG_CGROUP_NET_PRIO=y CONFIG_CAN=m +CONFIG_RFKILL=m +CONFIG_RFKILL_GPIO=m CONFIG_NET_9P=y CONFIG_NET_9P_VIRTIO=y CONFIG_PCI=y From e1cbe0e79628fc7399c758b2cb472f0e80efd88d Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Thu, 16 Jul 2026 23:19:02 +0200 Subject: [PATCH 276/521] FROMLIST: riscv: dts: spacemit: k3: add rfkill node for Bluetooth on Pico-ITX board On the K3 PICO-ITX board, by default the GPIO pin that connects to the RTL8852BE Bluetooth enable signal is driven low, resulting in impossibility to use Bluetooth. Add a DT node to expose it as an RFKILL device, which lets the Bluetooth driver or userspace toggle it as required. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260716213314.3027969-3-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index 7327eb65593c63..df6b6201ecc24e 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -43,6 +43,13 @@ regulator-max-microvolt = <5000000>; regulator-always-on; }; + + rfkill-usb-bt { + compatible = "rfkill-gpio"; + label = "rfkill-usb-bt"; + radio-type = "bluetooth"; + shutdown-gpios = <&gpio 0 30 GPIO_ACTIVE_HIGH>; + }; }; &i2c2 { From e03134546d9a88688a64a8b0a65bbb35a4681894 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Thu, 16 Jul 2026 23:19:03 +0200 Subject: [PATCH 277/521] FROMLIST: riscv: dts: spacemit: k3: add USB3 B and C controllers for Pico-ITX board The SpacemiT K3 has 4 USB3 controllers labelled A to D. On the K3 Pico-ITX board, the controllers B and C are used in USB 2 mode only respectively for the M.2 B-Key slot and for the RTL8852BE Bluetooth controller. Add the two controller nodes and the two corresponding USB2 PHY nodes (the PCIe/USB3 combo PHY driver is not yet merged) and enable them on the K3 Pico-ITX board. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260716213314.3027969-4-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 18 ++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 64 ++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index df6b6201ecc24e..e60d57b0891b25 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -369,6 +369,24 @@ status = "okay"; }; +&usb3b { + maximum-speed = "high-speed"; + status = "okay"; +}; + +&usb3b_u2phy { + status = "okay"; +}; + +&usb3c { + maximum-speed = "high-speed"; + status = "okay"; +}; + +&usb3c_u2phy { + status = "okay"; +}; + &usb3d_u2phy { status = "okay"; }; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index d7beabc88d866a..ab3d71282da03c 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -634,6 +634,70 @@ }; }; + usb3b: usb@81400000 { + compatible = "spacemit,k3-dwc3"; + reg = <0x0 0x81400000 0x0 0x10000>; + interrupts = <125 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_apmu CLK_APMU_USB3_PORTB_BUS>; + clock-names = "usbdrd30"; + resets = <&syscon_apmu RESET_APMU_USB3_B_AHB>, + <&syscon_apmu RESET_APMU_USB3_B_VCC>, + <&syscon_apmu RESET_APMU_USB3_B_PHY>; + reset-names = "ahb", "vcc", "phy"; + phys = <&usb3b_u2phy>; + phy-names = "usb2-phy"; + phy_type = "utmi"; + snps,dis_enblslpm_quirk; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; + snps,parkmode-disable-ss-quirk; + dr_mode = "host"; + status = "disabled"; + }; + + usb3b_u2phy: phy@81500000 { + compatible = "spacemit,k3-usb2-phy"; + reg = <0x0 0x81500000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_USB3_PORTB_BUS>; + #phy-cells = <0>; + status = "disabled"; + }; + + usb3c: usb@81700000 { + compatible = "spacemit,k3-dwc3"; + reg = <0x0 0x81700000 0x0 0x10000>; + interrupts = <148 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_apmu CLK_APMU_USB3_PORTC_BUS>; + clock-names = "usbdrd30"; + resets = <&syscon_apmu RESET_APMU_USB3_C_AHB>, + <&syscon_apmu RESET_APMU_USB3_C_VCC>, + <&syscon_apmu RESET_APMU_USB3_C_PHY>; + reset-names = "ahb", "vcc", "phy"; + phys = <&usb3c_u2phy>; + phy-names = "usb2-phy"; + phy_type = "utmi"; + snps,dis_enblslpm_quirk; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; + snps,parkmode-disable-ss-quirk; + dr_mode = "host"; + status = "disabled"; + }; + + usb3c_u2phy: phy@81800000 { + compatible = "spacemit,k3-usb2-phy"; + reg = <0x0 0x81800000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_USB3_PORTC_BUS>; + #phy-cells = <0>; + status = "disabled"; + }; + usb3d: usb@81a00000 { compatible = "spacemit,k3-dwc3"; reg = <0x0 0x81a00000 0x0 0x10000>; From 432d30d743fd096cd9acea691210a3aeb0e8c9a5 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Thu, 16 Jul 2026 23:19:04 +0200 Subject: [PATCH 278/521] FROMLIST: riscv: dts: spacemit: k3: add rfkill node for WLAN on Pico-ITX board By default the GPIO pin that connects to the RTL8852BE WLAN enable signal is driven low, resulting in impossibility to connect to any network. Add a DT node to expose it as an RFKILL device, which lets the WiFi driver or userspace toggle it as required. Signed-off-by: Aurelien Jarno Link: https://lore.kernel.org/r/20260716213314.3027969-5-aurelien@aurel32.net Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index e60d57b0891b25..9dd60d1acb340e 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -44,6 +44,13 @@ regulator-always-on; }; + rfkill-pcie-wlan { + compatible = "rfkill-gpio"; + label = "rfkill-pcie-wlan"; + radio-type = "wlan"; + shutdown-gpios = <&gpio 1 3 GPIO_ACTIVE_HIGH>; + }; + rfkill-usb-bt { compatible = "rfkill-gpio"; label = "rfkill-usb-bt"; From 39519d615ac2b414a1c00a3fe81039656fef8f44 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Thu, 16 Jul 2026 14:47:10 +0800 Subject: [PATCH 279/521] FROMLIST: riscv: require vmemmap for NUMA balancing RISC-V randconfig can enable NUMA balancing while selecting SPARSEMEM_EXTREME without SPARSEMEM_VMEMMAP. In that layout, the section and node fields can consume enough page flag bits that LAST_CPUPID cannot fit in page->flags. The generic MM code then stores last_cpupid in struct page and emits the "Unfortunate NUMA and NUMA Balancing config" warning from mm/memory.c. NUMA balancing needs LAST_CPUPID storage, but this fallback grows struct page for a configuration that randconfig can select accidentally. Keep NUMA support available, but advertise RISC-V NUMA balancing support only when SPARSEMEM_VMEMMAP is enabled, where the page flag layout leaves room for LAST_CPUPID. This was originally seen with a RISC-V randconfig generated from KCONFIG_SEED=550012. Signed-off-by: Jia Wang Link: https://lore.kernel.org/r/20260716-numa_kconfig-v1-1-b0cd6d29accb@ultrarisc.com Signed-off-by: Han Gao --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 278e6157611ae0..ab610e340c9e8a 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -484,7 +484,7 @@ endchoice config NUMA bool "NUMA Memory Allocation and Scheduler Support" depends on SMP && MMU - select ARCH_SUPPORTS_NUMA_BALANCING + select ARCH_SUPPORTS_NUMA_BALANCING if SPARSEMEM_VMEMMAP select GENERIC_ARCH_NUMA select HAVE_SETUP_PER_CPU_AREA select NEED_PER_CPU_EMBED_FIRST_CHUNK From 571a7deebad360be886d52381580027805c2be49 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Thu, 16 Jul 2026 07:33:19 +0200 Subject: [PATCH 280/521] FROMLIST: riscv: time: Add missing __iomem in get_cycles() and get_cycles_hi() __iomem is missing while calling readl_relaxed() in get_cycles() and get_cycles_hi() and sparse complains. Add __iomem to silence the sparse warnings. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607160619.14G8GHp5-lkp@intel.com/ Signed-off-by: Nam Cao Link: https://lore.kernel.org/r/20260716053319.2178937-1-namcao@linutronix.de Signed-off-by: Han Gao --- arch/riscv/include/asm/timex.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/timex.h b/arch/riscv/include/asm/timex.h index a06697846e6952..d41acfb3959ddc 100644 --- a/arch/riscv/include/asm/timex.h +++ b/arch/riscv/include/asm/timex.h @@ -22,13 +22,13 @@ static inline cycles_t get_cycles(void) #else /* !CONFIG_64BIT */ static inline u32 get_cycles(void) { - return readl_relaxed(((u32 *)clint_time_val)); + return readl_relaxed(((u32 __iomem *)clint_time_val)); } #define get_cycles get_cycles static inline u32 get_cycles_hi(void) { - return readl_relaxed(((u32 *)clint_time_val) + 1); + return readl_relaxed(((u32 __iomem *)clint_time_val) + 1); } #define get_cycles_hi get_cycles_hi #endif /* CONFIG_64BIT */ From a77a6a9c75c816cfade326b46d41f44d6dcdf3e3 Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Thu, 16 Jul 2026 18:05:10 -0500 Subject: [PATCH 281/521] FROMLIST: PCI: Move Spacemit vendor and device IDs to linux/pci_ids.h Move the vendor and device ID for the existing Spacemit K1 PCIe Root Complex to include/linux/pci_ids.h. Also add K3's Root Complex device ID to this header. This is done so that these values can be referenced in the rtw89 driver to enable 36-bit DMA ability in it for WiFi to function on the K3 Pico ITX board. Acked-by: Bjorn Helgaas Signed-off-by: Anirudh Srinivasan Link: https://lore.kernel.org/r/20260716-rtw89-spacemit-k3-v2-1-392b577ebf75@oss.tenstorrent.com Signed-off-by: Han Gao --- drivers/pci/controller/dwc/pcie-spacemit-k1.c | 4 ---- include/linux/pci_ids.h | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index 7502d66bd0d0a8..bf2ab5cb59ce8b 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -21,10 +21,6 @@ #include "pcie-designware.h" -#define PCI_VENDOR_ID_SPACEMIT 0x201f -#define PCI_DEVICE_ID_SPACEMIT_K1 0x0001 -#define PCI_DEVICE_ID_SPACEMIT_K3 0x0002 - /* Offsets and field definitions for link management registers */ #define K1_PHY_AHB_IRQ_EN 0x0000 #define PCIE_INTERRUPT_EN BIT(0) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1c848bd40de45c..161daf96a27ff0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2642,6 +2642,10 @@ #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 +#define PCI_VENDOR_ID_SPACEMIT 0x201f +#define PCI_DEVICE_ID_SPACEMIT_K1 0x0001 +#define PCI_DEVICE_ID_SPACEMIT_K3 0x0002 + #define PCI_VENDOR_ID_HINT 0x3388 #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 From 2890119c0e6061fdc7a52ab83b9a86900315a80f Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Thu, 16 Jul 2026 18:05:11 -0500 Subject: [PATCH 282/521] FROMLIST: wifi: rtw89: pci: enable 36-bit DMA on spacemit K3 The Spacemit K3 Pico ITX Board has a RTL8852BE pcie card behind a PCIe root port, but the SoC doesn't have any 32 bit DMA addreseses which the rtw89 seems to use by default. Enable 36 bit DMA ability that the driver has when this particular root port is detected so that the driver can probe on this SoC. Tested-by: Aurelien Jarno Acked-by: Ping-Ke Shih Signed-off-by: Anirudh Srinivasan Link: https://lore.kernel.org/r/20260716-rtw89-spacemit-k3-v2-2-392b577ebf75@oss.tenstorrent.com Signed-off-by: Han Gao --- drivers/net/wireless/realtek/rtw89/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 43c61b3dc969f2..6470d09f17f3bb 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3312,6 +3312,10 @@ static bool rtw89_pci_is_dac_compatible_bridge(struct rtw89_dev *rtwdev) if (bridge->device == 0x2806) return true; break; + case PCI_VENDOR_ID_SPACEMIT: + if (bridge->device == PCI_DEVICE_ID_SPACEMIT_K3) + return true; + break; } return false; From 83beef71f4a16c47d3e34bbaa25a009ac6d79c5b Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Fri, 17 Jul 2026 20:40:06 +0800 Subject: [PATCH 283/521] FROMLIST: riscv: cmpxchg: Use .option arch for Zacas and Zabha Instead of adding these to -march globally, use .option arch to use instructions from these extensions only in code paths where we know they are available, like how it is done for most other extensions. TOOLCHAIN_HAS_{ZACAS,ZABHA} already depend on AS_HAS_OPTION_ARCH, so this is not a functionality regression even on older assemblers. Although the compiler is unlikely to generate atomics on its own accord, this aligns handling of Zacas and Zabha with most other extensions and improves consistency on how assembly code requiring extra extensions is written in kernel code. This is analogous to the use of __LSE_PREAMBLE or .arch_extension lse in arm64 code. Signed-off-by: Vivian Wang Link: https://lore.kernel.org/r/20260717-riscv-no-zacas-zabha-in-march-v1-1-82b5b0799fb6@iscas.ac.cn Signed-off-by: Han Gao --- arch/riscv/Makefile | 6 ------ arch/riscv/include/asm/cmpxchg.h | 14 +++++++++++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 729a46ac2b0d7d..3592f9788da32e 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -83,12 +83,6 @@ else riscv-march-$(CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI) := $(riscv-march-y)_zicsr_zifencei endif -# Check if the toolchain supports Zacas -riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZACAS) := $(riscv-march-y)_zacas - -# Check if the toolchain supports Zabha -riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZABHA) := $(riscv-march-y)_zabha - KBUILD_BASE_ISA = -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/') export KBUILD_BASE_ISA diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h index 8712cf9c69dcb3..662e160b05229b 100644 --- a/arch/riscv/include/asm/cmpxchg.h +++ b/arch/riscv/include/asm/cmpxchg.h @@ -23,7 +23,10 @@ riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) { \ __asm__ __volatile__ ( \ prepend \ + " .option push\n" \ + " .option arch, +zabha\n" \ " amoswap" swap_sfx " %0, %z2, %1\n" \ + " .option pop\n" \ swap_append \ : "=&r" (r), "+A" (*(p)) \ : "rJ" (n) \ @@ -141,7 +144,10 @@ \ __asm__ __volatile__ ( \ cas_prepend \ + " .option push\n" \ + " .option arch, +zacas, +zabha\n" \ " amocas" cas_sfx " %0, %z2, %1\n" \ + " .option pop\n" \ cas_append \ : "+&r" (r), "+A" (*(p)) \ : "rJ" (n) \ @@ -188,7 +194,10 @@ \ __asm__ __volatile__ ( \ cas_prepend \ + " .option push\n" \ + " .option arch, +zacas\n" \ " amocas" cas_sfx " %0, %z2, %1\n" \ + " .option pop\n" \ cas_append \ : "+&r" (r), "+A" (*(p)) \ : "rJ" (n) \ @@ -340,7 +349,10 @@ union __u128_halves { register unsigned long t4 asm ("t4") = __ho.high; \ \ __asm__ __volatile__ ( \ - " amocas.q" cas_sfx " %0, %z3, %2" \ + " .option push\n" \ + " .option arch, +zacas\n" \ + " amocas.q" cas_sfx " %0, %z3, %2\n" \ + " .option pop\n" \ : "+&r" (t3), "+&r" (t4), "+A" (*(p)) \ : "rJ" (t1), "rJ" (t2) \ : "memory"); \ From f6c872fe5cedfd3af0e2f53a72935491f797767b Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Tue, 21 Jul 2026 23:05:09 +0800 Subject: [PATCH 284/521] FROMLIST: riscv: hwprobe: Register unaligned probes before usermode The hwprobe vDSO data is populated by the first riscv_hwprobe syscall. Some values, such as MISALIGNED_VECTOR_PERF, may depend on the async vector unaligned access speed probe registered by check_unaligned_access_all_cpus(). That initcall currently runs at late_initcall level. However, rootfs_initcall enables usermode helpers before late initcalls run, so an early helper can execute userspace and call riscv_hwprobe first. In that case complete_hwprobe_vdso_data() consumes the initial pending_boot_probes reference, populates the vDSO cache, and marks it ready before the later async probe is registered. The eventual probe result then cannot update the already-ready cache. Move check_unaligned_access_all_cpus() to fs_initcall_sync. This still runs after clocksource_done_booting(), so the ktime_get_mono_fast_ns() benchmark uses a stable clocksource, but it runs before rootfs_initcall enables usermode helpers. Any async hwprobe probe is therefore registered before userspace can trigger the one-time vDSO cache population. Fixes: 6455c6c11827 ("riscv: Clean up & optimize unaligned scalar access probe") Signed-off-by: Rui Qi Link: https://lore.kernel.org/r/20260721150511.1607105-1-qirui.001@bytedance.com Signed-off-by: Han Gao --- arch/riscv/kernel/unaligned_access_speed.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index af437e909f0357..cde91b2c8270be 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -421,4 +421,10 @@ static int __init check_unaligned_access_all_cpus(void) return 0; } -late_initcall(check_unaligned_access_all_cpus); +/* + * Run after clocksource_done_booting() so measure_cycles() uses a stable + * clocksource, but before rootfs_initcall() enables usermode helpers. Those + * helpers can reach hwprobe and populate the vDSO cache, so async hwprobe + * probes must be registered first. + */ +fs_initcall_sync(check_unaligned_access_all_cpus); From 964efdcf07b45224d537586ac03ea50b056ca8db Mon Sep 17 00:00:00 2001 From: Gao Rui Date: Wed, 22 Jul 2026 14:40:26 +0800 Subject: [PATCH 285/521] FROMLIST: riscv: strnlen: fix ZBB path overflow and SIZE_MAX fallback This patch improves the RISC-V strnlen implementation: - Add explicit fallback to generic path when count == SIZE_MAX, avoiding minu instruction misbehavior. - Add overflow check for (s + count), ensuring safe fallback when address addition wraps around. - Refactor aligned boundary calculation to use (s + count - 1), preventing word loads beyond the valid range. - Add fast exit when all remaining bytes are within the first word. - Simplify generic path loop with clearer pointer/count handling. These changes fix potential off-by-one, overflow, and extreme input bugs, while keeping ZBB optimization for normal cases. Fixes: 5ba15d419fab ("riscv: lib: add strnlen() implementation") Signed-off-by: Gao Rui Link: https://lore.kernel.org/r/20260722064037.1111467-2-gao.rui@zte.com.cn Signed-off-by: Han Gao --- arch/riscv/lib/strnlen.S | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/arch/riscv/lib/strnlen.S b/arch/riscv/lib/strnlen.S index 53afa7b5b314d9..fc5d578bbffe43 100644 --- a/arch/riscv/lib/strnlen.S +++ b/arch/riscv/lib/strnlen.S @@ -17,6 +17,7 @@ SYM_FUNC_START(strnlen) __ALTERNATIVE_CFG("nop", "j strnlen_zbb", 0, RISCV_ISA_EXT_ZBB, IS_ENABLED(CONFIG_RISCV_ISA_ZBB) && IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZBB)) +strnlen_generic: /* * Returns @@ -27,20 +28,24 @@ SYM_FUNC_START(strnlen) * a1 - Max length of string * * Clobbers - * t0, t1, t2 + * t0, t1 */ - addi t1, a0, -1 - add t2, a0, a1 + mv t1, a0 + 1: - addi t1, t1, 1 - beq t1, t2, 2f + beqz a1, 2f + addi a1, a1, -1 + lbu t0, 0(t1) - bnez t0, 1b + beqz t0, 2f + + addi t1, t1, 1 + j 1b + 2: sub a0, t1, a0 ret - /* * Variant of strnlen using the ZBB extension if available */ @@ -73,6 +78,16 @@ strnlen_zbb: /* If maxlen is 0, return 0. */ beqz a1, 3f + /* + * Fallback to generic implementation when count is large enough to + * cause address overflow in the ZBB optimized path, or when count + * equals SIZE_MAX where the minu instruction misbehaves. + */ + li t5, -1 + beq a1, t5, strnlen_generic /* count == SIZE_MAX */ + add t4, a0, a1 + bltu t4, a0, strnlen_generic /* a0 + a1 overflow */ + /* Number of irrelevant bytes in the first word. */ andi t2, a0, SZREG-1 @@ -83,8 +98,12 @@ strnlen_zbb: sub t3, t3, t2 slli t2, t2, 3 - /* Aligned boundary. */ - add t4, a0, a1 + /* + * Aligned boundary. Use the address of the last valid byte + * (s + count - 1) to avoid loading a word past the count + * boundary in the loop below. count == 0 is handled above. + */ + addi t4, t4, -1 andi t4, t4, -SZREG /* Get the first word. */ @@ -120,6 +139,9 @@ strnlen_zbb: bgtu t3, a0, 2f + /* All remaining bytes are in the first word, no loop needed. */ + bgeu t0, t4, 2f + /* Prepare for the word comparison loop. */ addi t2, t0, SZREG li t3, -1 From fe260ba0b5ba20f4ff6808cef8c2169f4de48fd9 Mon Sep 17 00:00:00 2001 From: Gao Rui Date: Wed, 22 Jul 2026 14:40:27 +0800 Subject: [PATCH 286/521] FROMLIST: kunit: string: add strnlen SIZE_MAX test coverage Extend string_test_strnlen to validate strnlen behavior with SIZE_MAX input: - Add explicit test case for strnlen(s, SIZE_MAX). - Ensure fallback to generic path returns correct length when string has no NUL terminator. - Complements existing tests for non-terminated strings and boundary conditions. This improves KUnit coverage for extreme inputs and verifies the correctness of the new fallback logic in strnlen.S. Signed-off-by: Gao Rui Link: https://lore.kernel.org/r/20260722064037.1111467-3-gao.rui@zte.com.cn Signed-off-by: Han Gao --- lib/tests/string_kunit.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c index 0819ace5b02757..8e2e8980aa75a4 100644 --- a/lib/tests/string_kunit.c +++ b/lib/tests/string_kunit.c @@ -155,6 +155,16 @@ static void string_test_strnlen(struct kunit *test) for (size_t offset = 0; offset < STRING_TEST_MAX_OFFSET; offset++) { for (size_t len = 0; len <= STRING_TEST_MAX_LEN; len++) { + /* Test strings without NUL terminator */ + s = buf + buf_size - offset - len; + if (len > 0) + KUNIT_EXPECT_EQ(test, strnlen(s, len - 1), len - 1); + if (len > 1) + KUNIT_EXPECT_EQ(test, strnlen(s, len - 2), len - 2); + + KUNIT_EXPECT_EQ(test, strnlen(s, len), len); + + /* Test strings with NUL terminator */ s = buf + buf_size - 1 - offset - len; s[len] = '\0'; @@ -169,6 +179,9 @@ static void string_test_strnlen(struct kunit *test) KUNIT_EXPECT_EQ(test, strnlen(s, len + 2), len); KUNIT_EXPECT_EQ(test, strnlen(s, len + 10), len); + /* Test Count overflow fallback */ + KUNIT_EXPECT_EQ(test, strnlen(s, SSIZE_MAX), len); + s[len] = 'A'; } } From 303c0a3f6478a698f1fa968076d4166a1f7732bb Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:42:45 +0800 Subject: [PATCH 287/521] MARKER: FROMLIST: Start of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 3799140b031a7f..45f411301903e9 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,6 +550,7 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC + depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From d2efbe46dab739c303bebf4eca0bfc8aee453759 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 19 Dec 2018 16:31:14 +0800 Subject: [PATCH 288/521] FROMLIST: scsi: lpfc: Switch memcpy_fromio() to __read32_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit bc73905abf770192 ("[SCSI] lpfc 8.3.16: SLI Additions, updates, and code cleanup"), lpfc_memcpy_to_slim() have switched memcpy_toio() to __write32_copy() in order to prevent unaligned 64 bit copy. Recently, we found that lpfc_memcpy_from_slim() have similar issues, so let it switch memcpy_fromio() to __read32_copy(). As maintainer says, it seems that we can hardly see a real "unaligned 64 bit copy", but this patch is still useful. Because in our tests we found that lpfc doesn't support 128 bit access, but some optimized memcpy() use 128 bit access (at lease on Loongson). Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Signed-off-by: James Smart  Link: https://lore.kernel.org/all/1545208274-13736-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/scsi/lpfc/lpfc_compat.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h index 43cf46a3a71fea..0cd1e3c82d8759 100644 --- a/drivers/scsi/lpfc/lpfc_compat.h +++ b/drivers/scsi/lpfc/lpfc_compat.h @@ -91,8 +91,8 @@ lpfc_memcpy_to_slim( void __iomem *dest, void *src, unsigned int bytes) static inline void lpfc_memcpy_from_slim( void *dest, void __iomem *src, unsigned int bytes) { - /* actually returns 1 byte past dest */ - memcpy_fromio( dest, src, bytes); + /* convert bytes in argument list to word count for copy function */ + __ioread32_copy(dest, src, bytes / sizeof(uint32_t)); } #endif /* __BIG_ENDIAN */ From 2ef9d748d241efc49dbe9c17719ec0cb7031a398 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 6 Feb 2019 20:03:14 +0800 Subject: [PATCH 289/521] FROMLIST: MIPS: math-emu: Add madd/msub/nmadd/nmsub emulation for Loongson-3 Add madd.s/madd.d/msub.s/msub.d/nmadd.s/nmadd.d/nmsub.s/nmsub.d emulation for Loongson-3. MIPS R2 suggest these instructions be unfused, but Loongson-3 suggest these instructions be fused, which is similar to maddf/msubf in MIPS R6. Signed-off-by: Huacai Chen Signed-off-by: Pei Huang Link: https://lore.kernel.org/all/1549454594-9056-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/math-emu/cp1emu.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index c89e70df43d82b..cc0360e8dd62ca 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1451,6 +1451,7 @@ static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); } +#ifndef CONFIG_CPU_LOONGSON3 DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); @@ -1459,6 +1460,7 @@ DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); +#endif static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, mips_instruction ir, void __user **fault_addr) @@ -1513,6 +1515,20 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; +#ifdef CONFIG_CPU_LOONGSON3 + case madd_s_op: + handler = ieee754sp_madd; + goto scoptop; + case msub_s_op: + handler = ieee754sp_msub; + goto scoptop; + case nmadd_s_op: + handler = ieee754sp_nmadd; + goto scoptop; + case nmsub_s_op: + handler = ieee754sp_nmsub; + goto scoptop; +#else case madd_s_op: if (cpu_has_mac2008_only) handler = ieee754sp_madd; @@ -1537,6 +1553,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, else handler = fpemu_sp_nmsub; goto scoptop; +#endif scoptop: SPFROMREG(fr, MIPSInst_FR(ir)); @@ -1621,6 +1638,20 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; +#ifdef CONFIG_CPU_LOONGSON3 + case madd_d_op: + handler = ieee754dp_madd; + goto dcoptop; + case msub_d_op: + handler = ieee754dp_msub; + goto dcoptop; + case nmadd_d_op: + handler = ieee754dp_nmadd; + goto dcoptop; + case nmsub_d_op: + handler = ieee754dp_nmsub; + goto dcoptop; +#else case madd_d_op: if (cpu_has_mac2008_only) handler = ieee754dp_madd; @@ -1645,6 +1676,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, else handler = fpemu_dp_nmsub; goto dcoptop; +#endif dcoptop: DPFROMREG(fr, MIPSInst_FR(ir)); From 3c456650c8bf09c0ba05d408b69fcaa2eb2233a3 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 12 Mar 2020 17:40:53 +0800 Subject: [PATCH 290/521] BACKPORT: FROMLIST: MIPS: Add __cpu_full_name[] to make CPU names more human-readable In /proc/cpuinfo, we keep "cpu model" as is, since GCC should use it for -march=native. Besides, we add __cpu_full_name[] to describe the processor in a more human-readable manner. The full name is displayed as "model name" in cpuinfo, which is needed by some userspace tools such as "lscpu" and "gnome-system-monitor". The CPU frequency in "model name" is the default value (highest), and there is also a "CPU MHz" whose value can be changed by cpufreq. This is only used by Loongson now (ICT is dropped in cpu name, and cpu name can be overwritten by BIOS). Why drop ICT? At the beginning, Loongson is designed by ICT, but now all Loongson processors is designed by "Loongson Technology Corporation Limited" which is independent from ICT. We have search the code in https://codesearch.debian.net/, and the result is: 1, GCC searches the "cpu model" in cpuinfo, but it only matches "Loongson-2 V0.2" and so on, so drop "ICT" is comfortable; 2, Debian Installer searches the "cpu model" in cpuinfo and matches "ICT Loongson", but Yunqiang Su is modifying the code; 3, Valgrind searches the "cpu model" in cpuinfo and matches "ICT Loongson", and we are planning to fix it up. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1584006053-28887-1-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflicts in arch/mips/kernel/cpu-probe.c, arch/mips/loongson64/env.c, and arch/mips/loongson64/smp.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/include/asm/cpu-info.h | 2 ++ .../include/asm/mach-loongson64/boot_param.h | 1 + arch/mips/include/asm/time.h | 2 ++ arch/mips/kernel/cpu-probe.c | 30 ++++++++++++++----- arch/mips/kernel/proc.c | 6 ++++ arch/mips/kernel/time.c | 2 ++ arch/mips/loongson64/env.c | 13 ++++++++ arch/mips/loongson64/smp.c | 1 + arch/mips/loongson64/smp.h | 1 + 9 files changed, 51 insertions(+), 7 deletions(-) diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index 211b578af6aa0a..b7b60a84beb056 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -124,7 +124,9 @@ extern void cpu_report(void); extern void cpu_disable_mmid(void); extern const char *__cpu_name[]; +extern const char *__cpu_full_name[]; #define cpu_name_string() __cpu_name[raw_smp_processor_id()] +#define cpu_full_name_string() __cpu_full_name[raw_smp_processor_id()] struct seq_file; struct notifier_block; diff --git a/arch/mips/include/asm/mach-loongson64/boot_param.h b/arch/mips/include/asm/mach-loongson64/boot_param.h index 3a11ce85762be6..943c5762207324 100644 --- a/arch/mips/include/asm/mach-loongson64/boot_param.h +++ b/arch/mips/include/asm/mach-loongson64/boot_param.h @@ -66,6 +66,7 @@ struct efi_cpuinfo_loongson { u16 reserved_cores_mask; u32 cpu_clock_freq; /* cpu_clock */ u32 nr_cpus; + char cpuname[64]; } __packed; #define MAX_UARTS 64 diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 5e7193b759f337..93f8cd52e3d471 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -22,6 +22,8 @@ extern spinlock_t rtc_lock; */ extern void plat_time_init(void); +extern unsigned int mips_cpu_frequency; + /* * mips_hpt_frequency - must be set if you intend to use an R4k-compatible * counter as a timer interrupt source. diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 489612ed9d4987..88bae0ca721ca8 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -1253,32 +1253,44 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) switch (c->processor_id & PRID_REV_MASK) { case PRID_REV_LOONGSON2E: c->cputype = CPU_LOONGSON2EF; - __cpu_name[cpu] = "ICT Loongson-2"; + __cpu_name[cpu] = "Loongson-2"; set_elf_platform(cpu, "loongson2e"); set_isa(c, MIPS_CPU_ISA_III); c->fpu_msk31 |= FPU_CSR_CONDX; + __cpu_full_name[cpu] = "Loongson-2E"; break; case PRID_REV_LOONGSON2F: c->cputype = CPU_LOONGSON2EF; - __cpu_name[cpu] = "ICT Loongson-2"; + __cpu_name[cpu] = "Loongson-2"; set_elf_platform(cpu, "loongson2f"); set_isa(c, MIPS_CPU_ISA_III); c->fpu_msk31 |= FPU_CSR_CONDX; + __cpu_full_name[cpu] = "Loongson-2F"; break; case PRID_REV_LOONGSON3A_R1: c->cputype = CPU_LOONGSON64; - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3A R1 (Loongson-3A1000)"; c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | MIPS_ASE_LOONGSON_EXT); break; case PRID_REV_LOONGSON3B_R1: + c->cputype = CPU_LOONGSON64; + __cpu_name[cpu] = "Loongson-3"; + set_elf_platform(cpu, "loongson3b"); + set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3B R1 (Loongson-3B1000)"; + c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | + MIPS_ASE_LOONGSON_EXT); + break; case PRID_REV_LOONGSON3B_R2: c->cputype = CPU_LOONGSON64; - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3b"); set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3B R2 (Loongson-3B1500)"; c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | MIPS_ASE_LOONGSON_EXT); break; @@ -1706,15 +1718,17 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) switch (c->processor_id & PRID_REV_MASK) { case PRID_REV_LOONGSON3A_R2_0: case PRID_REV_LOONGSON3A_R2_1: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R2 (Loongson-3A2000)"; break; case PRID_REV_LOONGSON3A_R3_0: case PRID_REV_LOONGSON3A_R3_1: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R3 (Loongson-3A3000)"; break; } /* @@ -1733,9 +1747,10 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) LOONGSON_CONF6_INTIMER); break; case PRID_IMP_LOONGSON_64G: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R4 (Loongson-3A4000)"; decode_cpucfg(c); change_c0_config6(LOONGSON_CONF6_EXTIMER | LOONGSON_CONF6_INTIMER, LOONGSON_CONF6_INTIMER); @@ -1841,6 +1856,7 @@ EXPORT_SYMBOL(__ua_limit); #endif const char *__cpu_name[NR_CPUS]; +const char *__cpu_full_name[NR_CPUS]; const char *__elf_platform; const char *__elf_base_platform; diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 8f0a0001540c7b..9b2e85a2a932e6 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -15,6 +15,7 @@ #include #include #include +#include unsigned int vced_count, vcei_count; @@ -63,6 +64,11 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, fmt, __cpu_name[n], (version >> 4) & 0x0f, version & 0x0f, (fp_vers >> 4) & 0x0f, fp_vers & 0x0f); + if (__cpu_full_name[n]) + seq_printf(m, "model name\t\t: %s\n", __cpu_full_name[n]); + if (mips_cpu_frequency) + seq_printf(m, "CPU MHz\t\t\t: %u.%02u\n", + mips_cpu_frequency / 1000000, (mips_cpu_frequency / 10000) % 100); seq_printf(m, "BogoMIPS\t\t: %u.%02u\n", cpu_data[n].udelay_val / (500000/HZ), (cpu_data[n].udelay_val / (5000/HZ)) % 100); diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index ed339d7979f3f5..849c6be53b2968 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -120,6 +120,8 @@ EXPORT_SYMBOL(perf_irq); * 2) calculate a couple of cached variables for later usage */ +unsigned int mips_cpu_frequency; +EXPORT_SYMBOL_GPL(mips_cpu_frequency); unsigned int mips_hpt_frequency; EXPORT_SYMBOL_GPL(mips_hpt_frequency); diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c index 7abcca7ab4ed44..46bc2ffac652c0 100644 --- a/arch/mips/loongson64/env.c +++ b/arch/mips/loongson64/env.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ u32 cpu_clock_freq; EXPORT_SYMBOL(cpu_clock_freq); +char cpu_full_name[64]; struct efi_memory_map_loongson *loongson_memmap; struct loongson_system_configuration loongson_sysconf; @@ -171,6 +173,7 @@ static void __init lefi_fixup_fdt(struct system_loongson *system) void __init prom_lefi_init_env(void) { + char freq[12]; struct boot_params *boot_p; struct loongson_params *loongson_p; struct system_loongson *esys; @@ -265,6 +268,10 @@ void __init prom_lefi_init_env(void) loongson_sysconf.nr_nodes = (loongson_sysconf.nr_cpus + loongson_sysconf.cores_per_node - 1) / loongson_sysconf.cores_per_node; + if (!strncmp(ecpu->cpuname, "Loongson", 8)) + strncpy(cpu_full_name, ecpu->cpuname, sizeof(cpu_full_name)); + if (cpu_full_name[0] == 0) + strncpy(cpu_full_name, __cpu_full_name[0], sizeof(cpu_full_name)); loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits; if (loongson_sysconf.dma_mask_bits < 32 || @@ -288,8 +295,14 @@ void __init prom_lefi_init_env(void) loongson_sysconf.workarounds |= esys->workarounds; + mips_cpu_frequency = cpu_clock_freq; pr_info("CpuClock = %u\n", cpu_clock_freq); + /* Append default cpu frequency with round-off */ + sprintf(freq, " @ %uMHz", (cpu_clock_freq + 500000) / 1000000); + strncat(cpu_full_name, freq, sizeof(cpu_full_name)); + __cpu_full_name[0] = cpu_full_name; + /* Read the ID of PCI host bridge to detect bridge type */ id = readl(HOST_BRIDGE_CONFIG_ADDR); vendor = id & 0xffff; diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index e584299d0fde8e..c4e97a59df3055 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -428,6 +428,7 @@ static void loongson3_init_secondary(void) cpu_logical_map(cpu) % loongson_sysconf.cores_per_package); cpu_data[cpu].package = cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; + __cpu_full_name[cpu] = cpu_full_name; } static void loongson3_smp_finish(void) diff --git a/arch/mips/loongson64/smp.h b/arch/mips/loongson64/smp.h index 957bde81e0e4e5..6112d9d7dd2268 100644 --- a/arch/mips/loongson64/smp.h +++ b/arch/mips/loongson64/smp.h @@ -3,6 +3,7 @@ #define __LOONGSON_SMP_H_ /* for Loongson-3 smp support */ +extern char cpu_full_name[64]; extern unsigned long long smp_group[4]; /* 4 groups(nodes) in maximum in numa case */ From 3ee59800861795c1673683bfd4ec5e956568fe33 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 12 Mar 2020 17:41:57 +0800 Subject: [PATCH 291/521] FROMLIST: MIPS: Loongson: Add board_ebase_setup() support The EBase registers of old Loongson processor models before 3A2000 are 32bit and have no WG bit; those of newer models are 64bit and do have the WG bit. Unfortunately, dynamically allocated EBase addresses do not work well for the Loongson platform, because Loongson's memory layout is very limited below 0x20000000. The dynamically allocated EBase address above 0x20000000 is thus unmappable to a KSEG0/KSEG1 virtual address, but the cache error handler MUST be in KSEG1 (please see set_uncached_handler() in traps.c). Some might suggest that the cache error handler is hardly used so this is not a problem, but Loongson's MMIO configuration registers might be corrupted by set_uncached_handler(). To make Linux kernel on Loongson more robust, a board_ebase_setup() hook is added to ensure CKSEG0 is always used for EBase. This is also useful for configurations where firmware-provided EBase is not sane. Maybe this problem is present for all MIPSr2 processors, but it seems not all platforms have memory at physical address 0. So this patch only touches Loongson. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1584006117-28985-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/init.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/mips/loongson64/init.c b/arch/mips/loongson64/init.c index c7cc5a3d7817f1..9205195acca0b2 100644 --- a/arch/mips/loongson64/init.c +++ b/arch/mips/loongson64/init.c @@ -23,6 +23,16 @@ u32 node_id_offset; +static void __init mips_ebase_setup(void) +{ + ebase = CKSEG0; + + if (cpu_has_ebase_wg) + write_c0_ebase(ebase | MIPS_EBASE_WG); + + write_c0_ebase(ebase); +} + static void __init mips_nmi_setup(void) { void *base; @@ -147,6 +157,7 @@ void __init prom_init(void) setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE + 0x1e0), 0, 1024); register_smp_ops(&loongson3_smp_ops); + board_ebase_setup = mips_ebase_setup; board_nmi_handler_setup = mips_nmi_setup; } From 5bfa7873e514c7e5532e1c6bdd742d4dad7bd2ae Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 1 Nov 2020 11:33:58 +0800 Subject: [PATCH 292/521] FROMLIST: MIPS: Loongson64: Enlarge cross-package node distance NUMA node distances affect the NUMA balancing behaviors. The cost of cross-package memory access is very high, and our benchmarks show that 200 is a more appropriate value than 100 (for cross-package numa node distance) on Loongson64 platforms, so enlarge it. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1604201638-4001-3-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/numa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/numa.c b/arch/mips/loongson64/numa.c index 16ffb32cca5082..ae8f3758a27207 100644 --- a/arch/mips/loongson64/numa.c +++ b/arch/mips/loongson64/numa.c @@ -60,7 +60,7 @@ static int __init compute_node_distance(int row, int col) else if (package_row == package_col) return 40; else - return 100; + return 200; } static void __init init_topology_matrix(void) From b6f4a5bf437b56024d2610a7abeedc3849ef750d Mon Sep 17 00:00:00 2001 From: wangrui Date: Thu, 8 Apr 2021 19:30:59 +0800 Subject: [PATCH 293/521] BACKPORT: FROMLIST: MIPS: tlbex: Avoid access invalid address when pmd is modifying When user-space program accessing a virtual address and falls into TLB invalid exception handling. at almost the same time, if the pmd which that contains this virtual address is hit by THP scanning, and then a invalid address access may occurs in the tlb handler: CPU 0: (userspace) | CPU 1: (khugepaged) 1: | scan hit: set pmde to invalid_pmd_table | (by pmd_clear) 2: handle_tlbl(tlb invalid): | load pmde for huge page testing, | pmde doesn't contains _PAGE_HUGE | bit | 3: | collapsed: set pmde to huge page format 4: handle_tlbl(normal page case): | load pmde again as base address, | pmde doesn't contains an address, | access invalid address | This patch avoids the inconsistency of two memory loads by reusing the result of one load. Signed-off-by: wangrui Link: https://lore.kernel.org/all/20210212082058.40792-1-wangrui@loongson.cn/ Link: https://lore.kernel.org/all/CAAhV-H5a6f7SSaMfOJudFuqtzLqkDks_NkjsPe15gN3mJTGBDw@mail.gmail.com/ [Kexy: Resolved minor conflict in arch/mips/mm/tlbex.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/mm/tlbex.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 69ea54bdc0c361..33f613d6665b8b 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -676,13 +676,12 @@ static void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l, */ static void build_is_huge_pte(u32 **p, struct uasm_reloc **r, unsigned int tmp, - unsigned int pmd, int lid) + unsigned int pmde, int lid) { - UASM_i_LW(p, tmp, 0, pmd); if (use_bbit_insns()) { - uasm_il_bbit1(p, r, tmp, ilog2(_PAGE_HUGE), lid); + uasm_il_bbit1(p, r, pmde, ilog2(_PAGE_HUGE), lid); } else { - uasm_i_andi(p, tmp, tmp, _PAGE_HUGE); + uasm_i_andi(p, tmp, pmde, _PAGE_HUGE); uasm_il_bnez(p, r, tmp, lid); } } @@ -1042,7 +1041,6 @@ EXPORT_SYMBOL_GPL(build_update_entries); struct mips_huge_tlb_info { int huge_pte; int restore_scratch; - bool need_reload_pte; }; static struct mips_huge_tlb_info @@ -1057,7 +1055,6 @@ build_fast_tlb_refill_handler (u32 **p, struct uasm_label **l, rv.huge_pte = scratch; rv.restore_scratch = 0; - rv.need_reload_pte = false; if (check_for_high_segbits) { UASM_i_MFC0(p, tmp, C0_BADVADDR); @@ -1263,7 +1260,6 @@ static void build_r4000_tlb_refill_handler(void) } else { htlb_info.huge_pte = GPR_K0; htlb_info.restore_scratch = 0; - htlb_info.need_reload_pte = true; vmalloc_mode = refill_noscratch; /* * create the plain linear handler @@ -1288,11 +1284,14 @@ static void build_r4000_tlb_refill_handler(void) build_get_pgde32(&p, GPR_K0, GPR_K1); /* get pgd in GPR_K1 */ #endif + UASM_i_LW(&p, GPR_K0, 0, GPR_K1); /* get pmd entry in GPR_K0 */ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT - build_is_huge_pte(&p, &r, GPR_K0, GPR_K1, label_tlb_huge_update); + build_is_huge_pte(&p, &r, GPR_K1, GPR_K0, label_tlb_huge_update); #endif - build_get_ptep(&p, GPR_K0, GPR_K1); + GET_CONTEXT(&p, GPR_K1); /* get context reg */ + build_adjust_context(&p, GPR_K1); + UASM_i_ADDU(&p, GPR_K1, GPR_K0, GPR_K1); /* add in offset */ build_update_entries(&p, GPR_K0, GPR_K1); build_tlb_write_entry(&p, &l, &r, tlb_random); uasm_l_leave(&l, p); @@ -1300,8 +1299,6 @@ static void build_r4000_tlb_refill_handler(void) } #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT uasm_l_tlb_huge_update(&l, p); - if (htlb_info.need_reload_pte) - UASM_i_LW(&p, htlb_info.huge_pte, 0, GPR_K1); build_huge_update_entries(&p, htlb_info.huge_pte, GPR_K1); build_huge_tlb_write_entry(&p, &l, &r, GPR_K0, tlb_random, htlb_info.restore_scratch); @@ -2001,20 +1998,20 @@ build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l, build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */ #endif + UASM_i_LW(p, wr.r3, 0, wr.r2); /* get pmd entry in wr.r3 */ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT /* - * For huge tlb entries, pmd doesn't contain an address but + * For huge tlb entries, pmde doesn't contain an address but * instead contains the tlb pte. Check the PAGE_HUGE bit and * see if we need to jump to huge tlb processing. */ - build_is_huge_pte(p, r, wr.r1, wr.r2, label_tlb_huge_update); + build_is_huge_pte(p, r, wr.r1, wr.r3, label_tlb_huge_update); #endif UASM_i_MFC0(p, wr.r1, C0_BADVADDR); - UASM_i_LW(p, wr.r2, 0, wr.r2); UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT - PTE_T_LOG2); uasm_i_andi(p, wr.r1, wr.r1, (PTRS_PER_PTE - 1) << PTE_T_LOG2); - UASM_i_ADDU(p, wr.r2, wr.r2, wr.r1); + UASM_i_ADDU(p, wr.r2, wr.r3, wr.r1); #ifdef CONFIG_SMP uasm_l_smp_pgtable_change(l, *p); From b730c0a2f376d17b10ae8076e7b06f6c8285428a Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:43:01 +0800 Subject: [PATCH 294/521] MARKER: FROMLIST: End of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 45f411301903e9..3799140b031a7f 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,7 +550,6 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC - depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From 662a3bf7f6edc626592023d117f9914af17077fd Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 29 Oct 2020 16:29:11 +0800 Subject: [PATCH 295/521] LOONGSON: LoongArch: Add CPU HWMon platform driver This add CPU HWMon (temperature sensor) platform driver for Loongson-3. Tested-by: Xi Ruoyao Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commit/2a6c1c74d93a21613a523aebc6494d654f35cf1a Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/loongarch/Kconfig | 8 + drivers/platform/loongarch/Makefile | 1 + drivers/platform/loongarch/cpu_hwmon.c | 194 +++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 drivers/platform/loongarch/cpu_hwmon.c diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig index 447528797d07aa..b87f51c4e4b662 100644 --- a/drivers/platform/loongarch/Kconfig +++ b/drivers/platform/loongarch/Kconfig @@ -16,6 +16,14 @@ menuconfig LOONGARCH_PLATFORM_DEVICES if LOONGARCH_PLATFORM_DEVICES +config CPU_HWMON + bool "Loongson CPU HWMon Driver" + depends on MACH_LOONGSON64 + select HWMON + default y + help + Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver. + config LOONGSON_LAPTOP tristate "Generic Loongson-3 Laptop Driver" depends on ACPI_EC diff --git a/drivers/platform/loongarch/Makefile b/drivers/platform/loongarch/Makefile index f43ab03db1a2d5..8038291bae3aa8 100644 --- a/drivers/platform/loongarch/Makefile +++ b/drivers/platform/loongarch/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o obj-$(CONFIG_LOONGSON_LAPTOP) += loongson-laptop.o diff --git a/drivers/platform/loongarch/cpu_hwmon.c b/drivers/platform/loongarch/cpu_hwmon.c new file mode 100644 index 00000000000000..93a05993745aaa --- /dev/null +++ b/drivers/platform/loongarch/cpu_hwmon.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Technology Corporation Limited + */ +#include +#include +#include +#include +#include + +#include + +static int nr_packages; +static struct device *cpu_hwmon_dev; + +static int loongson3_cpu_temp(int cpu) +{ + u32 reg; + + reg = iocsr_read32(LOONGARCH_IOCSR_CPUTEMP) & 0xff; + + return (int)((s8)reg) * 1000; +} + +static ssize_t cpu_temp_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + return sprintf(buf, "CPU %d Temperature\n", id); +} + +static ssize_t get_cpu_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + int value = loongson3_cpu_temp(id); + return sprintf(buf, "%d\n", value); +} + +static SENSOR_DEVICE_ATTR(temp1_input, 0444, get_cpu_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_label, 0444, cpu_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_input, 0444, get_cpu_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_label, 0444, cpu_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_input, 0444, get_cpu_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp3_label, 0444, cpu_temp_label, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp4_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp7_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp7_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp8_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp8_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp9_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp9_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp10_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp10_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp11_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp11_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp12_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp12_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp13_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp13_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp14_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp14_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp15_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp15_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp16_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp16_label, 0444, cpu_temp_label, NULL, 4); + +static struct attribute *cpu_hwmon_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_label.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_label.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp7_label.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp8_label.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + &sensor_dev_attr_temp9_label.dev_attr.attr, + &sensor_dev_attr_temp10_input.dev_attr.attr, + &sensor_dev_attr_temp10_label.dev_attr.attr, + &sensor_dev_attr_temp11_input.dev_attr.attr, + &sensor_dev_attr_temp11_label.dev_attr.attr, + &sensor_dev_attr_temp12_input.dev_attr.attr, + &sensor_dev_attr_temp12_label.dev_attr.attr, + &sensor_dev_attr_temp13_input.dev_attr.attr, + &sensor_dev_attr_temp13_label.dev_attr.attr, + &sensor_dev_attr_temp14_input.dev_attr.attr, + &sensor_dev_attr_temp14_label.dev_attr.attr, + &sensor_dev_attr_temp15_input.dev_attr.attr, + &sensor_dev_attr_temp15_label.dev_attr.attr, + &sensor_dev_attr_temp16_input.dev_attr.attr, + &sensor_dev_attr_temp16_label.dev_attr.attr, + NULL +}; +static umode_t cpu_hwmon_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + int id = i / 2; + + if (id < nr_packages) + return attr->mode; + return 0; +} + +static struct attribute_group cpu_hwmon_group = { + .attrs = cpu_hwmon_attributes, + .is_visible = cpu_hwmon_is_visible, +}; + +static const struct attribute_group *cpu_hwmon_groups[] = { + &cpu_hwmon_group, + NULL +}; + +static int cpu_initial_threshold = 72000; +static int cpu_thermal_threshold = 96000; +module_param(cpu_thermal_threshold, int, 0644); +MODULE_PARM_DESC(cpu_thermal_threshold, "cpu thermal threshold (96000 (default))"); + +static struct delayed_work thermal_work; + +static void do_thermal_timer(struct work_struct *work) +{ + int i, value, temp_max = 0; + + for (i=0; i temp_max) + temp_max = value; + } + + if (temp_max <= cpu_thermal_threshold) + schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000)); + else + orderly_poweroff(true); +} + +static int __init loongson_hwmon_init(void) +{ + int i, value, temp_max = 0; + + pr_info("Loongson Hwmon Enter...\n"); + + nr_packages = loongson_sysconf.nr_cpus / + loongson_sysconf.cores_per_package; + + cpu_hwmon_dev = hwmon_device_register_with_groups(NULL, "cpu_hwmon", + NULL, cpu_hwmon_groups); + if (IS_ERR(cpu_hwmon_dev)) { + pr_err("Hwmon register fail with %ld!\n", PTR_ERR(cpu_hwmon_dev)); + return PTR_ERR(cpu_hwmon_dev); + } + + for (i = 0; i < nr_packages; i++) { + value = loongson3_cpu_temp(i); + if (value > temp_max) + temp_max = value; + } + + pr_info("Initial CPU temperature is %d (highest).\n", temp_max); + if (temp_max > cpu_initial_threshold) + cpu_thermal_threshold += temp_max - cpu_initial_threshold; + + INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer); + schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000)); + + return 0; +} + +static void __exit loongson_hwmon_exit(void) +{ + cancel_delayed_work_sync(&thermal_work); + hwmon_device_unregister(cpu_hwmon_dev); +} + +module_init(loongson_hwmon_init); +module_exit(loongson_hwmon_exit); + +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("Loongson CPU Hwmon driver"); +MODULE_LICENSE("GPL"); From cb358fe901637085f7035eb2cf34bae6226ba7e5 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 28 Jan 2024 14:07:46 +0800 Subject: [PATCH 296/521] LOONGSON: drivers/firmware: Move sysfb_init() from device_initcall to fs_initcall Consider a configuration like this: 1, efifb (or simpledrm) is built-in; 2, a native display driver (such as radeon) is also built-in. As Javier said, this is not a common configuration (the native display driver is usually built as a module), but it can happen and cause some trouble. In this case, since efifb, radeon and sysfb are all in device_initcall() level, the order in practise is like this: efifb registered at first, but no "efi-framebuffer" device yet. radeon registered later, and /dev/fb0 created. sysfb_init() comes at last, it registers "efi-framebuffer" and then causes an error message "efifb: a framebuffer is already registered". Make sysfb_init() to be subsys_ initcall_sync() can avoid this. And Javier Martinez Canillas is trying to make a more general solution in commit 873eb3b11860 ("fbdev: Disable sysfb device registration when removing conflicting FBs"). However, this patch still makes sense because it can make the screen display as early as possible (We cannot move to subsys_initcall, since sysfb_init() should be executed after PCI enumeration). This is a better version of commit 60aebc9559492cea ("drivers/firmware: Move sysfb_init() from device_initcall to subsys_initcall_sync") since the previous commit leads to blank displays on some systems. The reason is that vgaarb initialization is also a subsys_initcall_sync function so sysfb_disable() is sometimes missed. So here we move sysfb_init() to an fs_initcall function which is ensured after vgaarb initialization. Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commit/8bea46f981eab88e6cc92f9ab287db16edff5cf2 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/firmware/sysfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 8833582c188301..7d529d8bed5124 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -222,4 +222,4 @@ static __init int sysfb_init(void) } /* must execute after PCI subsystem for EFI quirks */ -device_initcall(sysfb_init); +fs_initcall(sysfb_init); From cf1a8b3570fa2d2fe244d6611023a04d3f9fbbee Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 22 Feb 2021 10:53:47 +0800 Subject: [PATCH 297/521] LOONGSON: drm/radeon: Workaround radeon driver bug for Loongson Radeon driver can not handle the interrupt is faster than DMA data, so irq handler must update an old ih.rptr value in IH_RB_RPTR register to enable interrupt again when interrupt is faster than DMA data. Signed-off-by: Huacai Chen Signed-off-by: Zhijie Zhang Link: https://github.com/chenhuacai/linux/commit/6266d0082b020ad68a3b3c6f314ba299b9d06d3d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/radeon/cik.c | 1 + drivers/gpu/drm/radeon/evergreen.c | 1 + drivers/gpu/drm/radeon/r600.c | 1 + drivers/gpu/drm/radeon/si.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 51a3e0fc2f56be..0b804e951b7b6f 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -8093,6 +8093,7 @@ int cik_irq_process(struct radeon_device *rdev) if (queue_thermal) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 3cbc6eedbf6639..ee79eeeb838ed3 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4927,6 +4927,7 @@ int evergreen_irq_process(struct radeon_device *rdev) if (queue_thermal && rdev->pm.dpm_enabled) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 8b62f7faa5b99f..88e38fb3f3476b 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -4328,6 +4328,7 @@ int r600_irq_process(struct radeon_device *rdev) if (queue_thermal && rdev->pm.dpm_enabled) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 26197aceb001cc..2d7eeb781969d1 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -6423,6 +6423,7 @@ int si_irq_process(struct radeon_device *rdev) if (queue_thermal && rdev->pm.dpm_enabled) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ From 26f89b2ce5818715615919e8b4293935c75fd21e Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 26 May 2026 18:49:37 +0800 Subject: [PATCH 298/521] LOONGSON: cpufreq: loongson3: SMC boost test As instructed for testing. Upstreamable patch pending. Signed-off-by: Mingcong Bai --- drivers/cpufreq/loongson3_cpufreq.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c index 1e8715ea1b771d..e5c01eae05db69 100644 --- a/drivers/cpufreq/loongson3_cpufreq.c +++ b/drivers/cpufreq/loongson3_cpufreq.c @@ -21,9 +21,9 @@ union smc_message { u32 value; struct { - u32 id : 4; + u32 id : 8; u32 info : 4; - u32 val : 16; + u32 val : 12; u32 cmd : 6; u32 extra : 1; u32 complete : 1; @@ -220,8 +220,9 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext static unsigned int loongson3_cpufreq_get(unsigned int cpu) { int ret; + unsigned int core = cpu_logical_map(cpu); - ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0); return ret * KILO; } @@ -229,9 +230,9 @@ static unsigned int loongson3_cpufreq_get(unsigned int cpu) static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) { int ret; + unsigned int core = cpu_logical_map(policy->cpu); - ret = do_service_request(cpu_data[policy->cpu].core, - FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0); return (ret >= 0) ? 0 : ret; } From dc8423eb7ad0fbac33cdde30e424876dae9f0733 Mon Sep 17 00:00:00 2001 From: Chen Baozi Date: Fri, 14 Jul 2023 08:33:58 +0800 Subject: [PATCH 299/521] BACKPORT: PHYTIUM: arm64: phytium: Add support for Phytium SoC family This patch adds supoort for the Phytium SoC family. Signed-off-by: Chen Baozi Change-Id: Ibe06dc2e54a413a8c1257fef3aade99e6ca6480f Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/154f8e5d365d57d38ad6fabc230861c9643db154 [Kexy: Resolved minor conflict in arch/arm64/Kconfig.platforms] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/Kconfig.platforms | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 72c812e76b0b11..cff15501771701 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -318,6 +318,14 @@ config ARCH_PENSANDO cores for a minimal latency/jitter datapath, and network interfaces up to 200 Gb/s. +config ARCH_PHYTIUM + bool "Phytium SoC Family" + help + This enables support for Phytium ARMv8 SoC family, including: + - Phytium Server SoC Family + - Phytium Desktop SoC Family + - Phytium Embedded SoC Family + config ARCH_QCOM bool "Qualcomm Platforms" select GPIOLIB From 7d9e57887941bce099cb2c00174857ca7a4de10c Mon Sep 17 00:00:00 2001 From: Chen Baozi Date: Fri, 14 Jul 2023 08:33:59 +0800 Subject: [PATCH 300/521] PHYTIUM: PCI: Add Phytium vendor ID Update pci_ids.h with the vendor ID for Phytium. Signed-off-by: Chen Baozi Change-Id: Ie0ea6afa71a8ba2f66f8426c1db063cd734d985c Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b570810d41c9cb46f05c31b51d156c6b4441486a Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- include/linux/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 161daf96a27ff0..ab797f414f19bd 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -3273,4 +3273,6 @@ #define PCI_VENDOR_ID_NCUBE 0x10ff +#define PCI_VENDOR_ID_PHYTIUM 0x1db7 + #endif /* _LINUX_PCI_IDS_H */ From e0bd69325e8c145a0217be27ea340042da123676 Mon Sep 17 00:00:00 2001 From: Chen Baozi Date: Fri, 14 Jul 2023 08:34:22 +0800 Subject: [PATCH 301/521] BACKPORT: PHYTIUM: net: stmmac: Add Phytium GMAC glue layer This patch adds support for Phytium GMAC controller which derived from Synopsys Designware MAC 10/100/1000 Universal. As required by SBBR, ACPI DSDT is supported as well as device tree. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Signed-off-by: Chen Baozi Change-Id: Id72b75c7a398c98b94df07ae92d7aa72bdc4d8ad Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/bf64fd4ae38b9dee89dfe8b17b79a50eec56961e [Kexy: Resolved minor conflict in MAINTAINERS] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai [Icenowy: Remove lpi_irq reference] Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/net/ethernet/stmicro/stmmac/Kconfig drivers/net/ethernet/stmicro/stmmac/Makefile ] Signed-off-by: Mingcong Bai --- MAINTAINERS | 6 + drivers/net/ethernet/stmicro/stmmac/Kconfig | 10 + drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + .../ethernet/stmicro/stmmac/dwmac-phytium.c | 228 ++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c diff --git a/MAINTAINERS b/MAINTAINERS index fcd8e3aee952c0..d5d1425952e24a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3304,6 +3304,12 @@ S: Maintained W: http://www.digriz.org.uk/ts78xx/kernel F: arch/arm/mach-orion5x/ts78xx-* +ARM/PHYTIUM SOC SUPPORT +M: Chen Baozi +S: Maintained +W: https://www.phytium.com.cn +F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c + ARM/QUALCOMM CHROMEBOOK SUPPORT R: cros-qcom-dts-watchers@chromium.org F: arch/arm64/boot/dts/qcom/sc7180* diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index e3dd5adda5acaf..8fa5343bf65bc5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -142,6 +142,16 @@ config DWMAC_NUVOTON for the stmmac device driver. The nuvoton-dwmac driver is used for MA35 series SoCs. +config DWMAC_PHYTIUM + tristate "Phytium dwmac support" + default ARCH_PHYTIUM + depends on (OF || ACPI) && (ARCH_PHYTIUM || COMPILE_TEST) + help + Support for GMAC controller on Phytium SoCs. + + This selects the Phytium GMAC glue layer support for the + stmmac device driver. + config DWMAC_QCOM_ETHQOS tristate "Qualcomm ETHQOS support" default ARCH_QCOM diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index a1cea2f57252e6..a5b812a44701a1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o obj-$(CONFIG_DWMAC_NUVOTON) += dwmac-nuvoton.o +obj-$(CONFIG_DWMAC_PHYTIUM) += dwmac-phytium.o obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o obj-$(CONFIG_DWMAC_RENESAS_GBETH) += dwmac-renesas-gbeth.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c new file mode 100644 index 00000000000000..e04a91464d673e --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium DWMAC specific glue layer + * + * Copyright (C) 2022, Phytium Technology Co., Ltd. + * + * Chen Baozi + */ + +#include +#include +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" + +static int phytium_get_mac_mode(struct fwnode_handle *fwnode) +{ + const char *pm; + int err, i; + + err = fwnode_property_read_string(fwnode, "mac-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phy_modes(i))) + return i; + } + + return -ENODEV; +} + +static int phytium_dwmac_acpi_phy(struct plat_stmmacenet_data *plat, + struct fwnode_handle *np, struct device *dev) +{ + plat->mdio_bus_data = + devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); + + if (!plat->mdio_bus_data) + return -ENOMEM; + + return 0; +} + +static int phytium_dwmac_probe(struct platform_device *pdev) +{ + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); + struct plat_stmmacenet_data *plat; + struct stmmac_resources stmmac_res; + struct device_node *np = pdev->dev.of_node; + u64 clk_freq; + char clk_name[20]; + int ret; + + plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + + plat->dma_cfg = + devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); + if (!plat->dma_cfg) + return -ENOMEM; + + plat->axi = devm_kzalloc(&pdev->dev, sizeof(*plat->axi), GFP_KERNEL); + if (!plat->axi) + return -ENOMEM; + + plat->phy_interface = device_get_phy_mode(&pdev->dev); + if (plat->phy_interface < 0) + return plat->phy_interface; + + plat->interface = phytium_get_mac_mode(fwnode); + if (plat->interface < 0) + plat->interface = plat->phy_interface; + + /* Configure PHY if using device-tree */ + if (pdev->dev.of_node) { + plat->phy_node = of_parse_phandle(np, "phy-handle", 0); + plat->phylink_node = np; + } + + if (pdev->dev.of_node) { + plat->bus_id = of_alias_get_id(np, "ethernet"); + if (plat->bus_id < 0) + plat->bus_id = 0; + } else if (fwnode_property_read_u32(fwnode, "bus_id", &plat->bus_id)) { + plat->bus_id = 2; + } + + plat->phy_addr = -1; + plat->clk_csr = -1; + plat->has_gmac = 1; + plat->enh_desc = 1; + plat->bugged_jumbo = 1; + plat->pmt = 1; + plat->force_sf_dma_mode = 1; + + if (fwnode_property_read_u32(fwnode, "max-speed", &plat->max_speed)) + plat->max_speed = -1; + + if (fwnode_property_read_u32(fwnode, "max-frame-size", &plat->maxmtu)) + plat->maxmtu = JUMBO_LEN; + + if (fwnode_property_read_u32(fwnode, "snps,multicast-filter-bins", + &plat->multicast_filter_bins)) + plat->multicast_filter_bins = HASH_TABLE_SIZE; + + if (fwnode_property_read_u32(fwnode, "snps,perfect-filter-entries", + &plat->unicast_filter_entries)) + plat->unicast_filter_entries = 1; + + if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", + &plat->tx_fifo_size)) + plat->tx_fifo_size = 0x1000; + + if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", + &plat->rx_fifo_size)) + plat->rx_fifo_size = 0x1000; + + if (phytium_dwmac_acpi_phy(plat, fwnode, &pdev->dev)) + return -ENODEV; + + plat->rx_queues_to_use = 1; + plat->tx_queues_to_use = 1; + plat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + plat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + + if (fwnode_property_read_u64(fwnode, "clock-frequency", &clk_freq)) + clk_freq = 125000000; + + /* Set system clock */ + snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", + plat->bus_id); + + plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, + 0, clk_freq); + if (IS_ERR(plat->stmmac_clk)) { + dev_warn(&pdev->dev, "Fail to register stmmac-clk\n"); + plat->stmmac_clk = NULL; + } + + ret = clk_prepare_enable(plat->stmmac_clk); + if (ret) { + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; + } + + plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk); + plat->clk_ptp_ref = NULL; + + if (fwnode_property_read_u32(fwnode, "snps,pbl", &plat->dma_cfg->pbl)) + plat->dma_cfg->pbl = 16; + + fwnode_property_read_u32(fwnode, "snps,txpbl", &plat->dma_cfg->txpbl); + fwnode_property_read_u32(fwnode, "snps,rxpbl", &plat->dma_cfg->rxpbl); + + plat->dma_cfg->pblx8 = + !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); + plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); + plat->dma_cfg->fixed_burst = + fwnode_property_read_bool(fwnode, "snps,fixed-burst"); + plat->dma_cfg->mixed_burst = + fwnode_property_read_bool(fwnode, "snps,mixed-burst"); + + plat->axi->axi_lpi_en = false; + plat->axi->axi_xit_frm = false; + plat->axi->axi_wr_osr_lmt = 7; + plat->axi->axi_rd_osr_lmt = 7; + plat->axi->axi_blen[0] = 16; + + memset(&stmmac_res, 0, sizeof(stmmac_res)); + stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0); + stmmac_res.irq = platform_get_irq(pdev, 0); + if (stmmac_res.irq < 0) { + dev_err(&pdev->dev, "IRQ not found.\n"); + return -ENXIO; + } + stmmac_res.wol_irq = stmmac_res.irq; + + return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); +} + +int phytium_dwmac_remove(struct platform_device *pdev) +{ + int ret; + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + struct plat_stmmacenet_data *plat = priv->plat; + + ret = stmmac_pltfr_remove(pdev); + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id phytium_dwmac_of_match[] = { + { .compatible = "phytium,gmac" }, + {} +}; +MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_dwmac_acpi_ids[] = { + { .id = "PHYT0004" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); +#endif + +static struct platform_driver phytium_dwmac_driver = { + .probe = phytium_dwmac_probe, + .remove = phytium_dwmac_remove, + .driver = { + .name = "phytium-dwmac", + .of_match_table = of_match_ptr(phytium_dwmac_of_match), + .acpi_match_table = ACPI_PTR(phytium_dwmac_acpi_ids), + }, +}; +module_platform_driver(phytium_dwmac_driver); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium DWMAC specific glue layer"); +MODULE_LICENSE("GPL"); From c4724a0a3a13c3a814fee61ad5db30f7e3592473 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 20 Oct 2023 18:55:20 +0800 Subject: [PATCH 302/521] BACKPORT: PHYTIUM: net: stmmac: Add a barrier to make sure all access coherent Add a memory barrier to sync TX descriptor to avoid data error. Besides, increase the ring buffer size to avoid buffer overflow. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Signed-off-by: Chen Baozi Change-Id: I47bd63a472c915b7f88b16921e8fa63bf8b284d9 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/cc1c23a77c9282f285d22c046dc6908209e5a236 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai [ Mingcong Bai: Fixed a minor post-7.0.6 merge conflict in drivers/net/ethernet/stmicro/stmmac/common.h ] Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++-- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 927ea6230073f4..4446f7f66d811d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -59,10 +59,10 @@ static inline bool dwmac_is_xmac(enum dwmac_core_type core_type) */ #define DMA_MIN_TX_SIZE 64 #define DMA_MAX_TX_SIZE 1024 -#define DMA_DEFAULT_TX_SIZE 512 +#define DMA_DEFAULT_TX_SIZE 1024 #define DMA_MIN_RX_SIZE 64 #define DMA_MAX_RX_SIZE 1024 -#define DMA_DEFAULT_RX_SIZE 512 +#define DMA_DEFAULT_RX_SIZE 1024 #define STMMAC_NEXT_ENTRY(x, size) ((x + 1) & (size - 1)) #undef FRAME_FILTER_DEBUG diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index c4b613564f87ce..6bb35c9031d087 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -182,6 +182,10 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, else norm_set_tx_desc_len_on_ring(p, len); + /* The own bit must be the latest setting done when prepare the + * descriptor and then barrier is needed to make sure that all is coherent. + */ + wmb(); if (tx_own) p->des0 |= cpu_to_le32(TDES0_OWN); } From 8f6aa1a275c76dc1ef708df533229559cb5d1949 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Tue, 23 Jan 2024 09:24:03 +0800 Subject: [PATCH 303/521] BACKPORT: PHYTIUM: drivers: fix build errors 1. spi: Rename SPI_MASTER_GPIO_SS to SPI_CONTROLLER_GPIO_SS 82238 2. net: stmmac: clarify difference between "interface" and "phy_interface" a014c3555 3. net: stmmac: convert plat->phylink_node to fwnode e80af2a 4. net: stmmac: Make stmmac_pltfr_remove() return void 3246627f 5. Remove IOMMU_CAP_INTR_REMAP, instead rely on the interrupt subsystem 143c7bc 6. i2c: Switch .probe() to not take an id parameter 03c835f498b54 7. drivers: gpio: add #include Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/52a27ae1196d3714b22e430fb43ab9ac2e55e914 [Kexy: Resolved minor conflicts in drivers/gpio/gpio-phytium-core.c, drivers/input/keyboard/phytium-keypad.c, drivers/irqchip/irq-gic-phytium-2500-its.c, drivers/spi/spi-phytium.c, sound/soc/codecs/es8336.c, and sound/soc/codecs/es8388.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../net/ethernet/stmicro/stmmac/dwmac-phytium.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index e04a91464d673e..7ff6ad4d41bbf1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -73,14 +73,14 @@ static int phytium_dwmac_probe(struct platform_device *pdev) if (plat->phy_interface < 0) return plat->phy_interface; - plat->interface = phytium_get_mac_mode(fwnode); - if (plat->interface < 0) - plat->interface = plat->phy_interface; + plat->mac_interface = phytium_get_mac_mode(fwnode); + if (plat->mac_interface < 0) + plat->mac_interface = plat->phy_interface; /* Configure PHY if using device-tree */ if (pdev->dev.of_node) { plat->phy_node = of_parse_phandle(np, "phy-handle", 0); - plat->phylink_node = np; + plat->port_node = of_fwnode_handle(np); } if (pdev->dev.of_node) { @@ -184,16 +184,14 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -int phytium_dwmac_remove(struct platform_device *pdev) +void phytium_dwmac_remove(struct platform_device *pdev) { - int ret; struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); struct plat_stmmacenet_data *plat = priv->plat; - ret = stmmac_pltfr_remove(pdev); + stmmac_pltfr_remove(pdev); clk_unregister_fixed_rate(plat->stmmac_clk); - return ret; } #ifdef CONFIG_OF @@ -214,7 +212,7 @@ MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); static struct platform_driver phytium_dwmac_driver = { .probe = phytium_dwmac_probe, - .remove = phytium_dwmac_remove, + .remove_new = phytium_dwmac_remove, .driver = { .name = "phytium-dwmac", .of_match_table = of_match_ptr(phytium_dwmac_of_match), From f484102ef4b29af3bc3e593c6d7875027d4b7a39 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Mon, 5 Feb 2024 09:52:51 +0800 Subject: [PATCH 304/521] BACKPORT: PHYTIUM: Update phytium copyright info to 2024 Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f1b4bd2a3b847e061631dfea27f4c64bb75cfe9b [Kexy: Resolved minor conflicts in arch/arm64/boot/dts/phytium/pe2201-demo-ddr4.dts, arch/arm64/boot/dts/phytium/pe2201.dtsi, arch/arm64/boot/dts/phytium/pe2202-chillipi-edu-board.dts, arch/arm64/boot/dts/phytium/pe2202-demo-ddr4-local.dts, arch/arm64/boot/dts/phytium/pe2202-demo-ddr4.dts, arch/arm64/boot/dts/phytium/pe2202-miniitx-board.dts, arch/arm64/boot/dts/phytium/pe2202-power-board.dts, arch/arm64/boot/dts/phytium/pe2202.dtsi, arch/arm64/boot/dts/phytium/pe2204-come-board.dts, arch/arm64/boot/dts/phytium/pe2204-demo-ddr4-local.dts, arch/arm64/boot/dts/phytium/pe2204-demo-ddr4.dts, arch/arm64/boot/dts/phytium/pe2204-edu-board.dts, arch/arm64/boot/dts/phytium/pe2204-hanwei-board.dts, arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts, arch/arm64/boot/dts/phytium/pe2204-vpx-board.dts, arch/arm64/boot/dts/phytium/pe2204.dtsi, arch/arm64/boot/dts/phytium/pe220x.dtsi, arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts, drivers/char/hw_random/phytium-rng.c, drivers/char/ipmi/bt_bmc_phytium.c, drivers/char/ipmi/kcs_bmc_phytium.c, drivers/dma/phytium/phytium-ddmac.c, drivers/dma/phytium/phytium-ddmac.h, drivers/edac/phytium_pe220x_edac.c, drivers/gpio/gpio-phytium-core.c, drivers/gpio/gpio-phytium-core.h, drivers/gpio/gpio-phytium-pci.c, drivers/gpio/gpio-phytium-platform.c, drivers/gpio/gpio-phytium-sgpio.c, drivers/gpu/drm/phytium/pe220x_dc.c, drivers/gpu/drm/phytium/pe220x_dc.h, drivers/gpu/drm/phytium/pe220x_dp.c, drivers/gpu/drm/phytium/pe220x_dp.h, drivers/gpu/drm/phytium/pe220x_reg.h, drivers/gpu/drm/phytium/phytium_crtc.c, drivers/gpu/drm/phytium/phytium_crtc.h, drivers/gpu/drm/phytium/phytium_debugfs.c, drivers/gpu/drm/phytium/phytium_debugfs.h, drivers/gpu/drm/phytium/phytium_display_drv.c, drivers/gpu/drm/phytium/phytium_display_drv.h, drivers/gpu/drm/phytium/phytium_dp.c, drivers/gpu/drm/phytium/phytium_dp.h, drivers/gpu/drm/phytium/phytium_fb.c, drivers/gpu/drm/phytium/phytium_fb.h, drivers/gpu/drm/phytium/phytium_fbdev.c, drivers/gpu/drm/phytium/phytium_fbdev.h, drivers/gpu/drm/phytium/phytium_gem.c, drivers/gpu/drm/phytium/phytium_gem.h, drivers/gpu/drm/phytium/phytium_panel.c, drivers/gpu/drm/phytium/phytium_panel.h, drivers/gpu/drm/phytium/phytium_pci.c, drivers/gpu/drm/phytium/phytium_pci.h, drivers/gpu/drm/phytium/phytium_plane.c, drivers/gpu/drm/phytium/phytium_plane.h, drivers/gpu/drm/phytium/phytium_platform.c, drivers/gpu/drm/phytium/phytium_platform.h, drivers/gpu/drm/phytium/phytium_reg.h, drivers/gpu/drm/phytium/px210_dc.c, drivers/gpu/drm/phytium/px210_dc.h, drivers/gpu/drm/phytium/px210_dp.c, drivers/gpu/drm/phytium/px210_dp.h, drivers/gpu/drm/phytium/px210_reg.h, drivers/hwmon/tacho-phytium.c, drivers/hwspinlock/phytium_hwspinlock.c, drivers/i2c/busses/i2c-phytium-common.c, drivers/i2c/busses/i2c-phytium-core.h, drivers/i2c/busses/i2c-phytium-master.c, drivers/i2c/busses/i2c-phytium-pci.c, drivers/i2c/busses/i2c-phytium-platform.c, drivers/i2c/busses/i2c-phytium-slave.c, drivers/iio/adc/phytium-adc.c, drivers/input/keyboard/phytium-keypad.c, drivers/input/serio/phytium-ps2.c, drivers/irqchip/irq-gic-phytium-2500-its.c, drivers/irqchip/irq-gic-phytium-2500.c, drivers/irqchip/irq-phytium-ixic.c, drivers/mailbox/phytium_mailbox.c, drivers/media/platform/phytium/phytium_jpeg_core.c, drivers/media/platform/phytium/phytium_jpeg_core.h, drivers/media/platform/phytium/phytium_jpeg_reg.h, drivers/mfd/phytium_px210_i2s_lsd.c, drivers/mfd/phytium_px210_i2s_mmd.c, drivers/mmc/host/phytium-mci-pci.c, drivers/mmc/host/phytium-mci-plat.c, drivers/mmc/host/phytium-mci.c, drivers/mmc/host/phytium-mci.h, drivers/mmc/host/phytium-sdci.c, drivers/mmc/host/phytium-sdci.h, drivers/mtd/nand/raw/phytium_nand.c, drivers/mtd/nand/raw/phytium_nand.h, drivers/mtd/nand/raw/phytium_nand_pci.c, drivers/mtd/nand/raw/phytium_nand_plat.c, drivers/net/can/phytium/phytium_can.c, drivers/net/can/phytium/phytium_can.h, drivers/net/can/phytium/phytium_can_pci.c, drivers/net/can/phytium/phytium_can_platform.c, drivers/pci/controller/pcie-phytium-ep.c, drivers/pci/controller/pcie-phytium-ep.h, drivers/pci/controller/pcie-phytium-register.h, drivers/pwm/pwm-phytium.c, drivers/remoteproc/homo_remoteproc.c, drivers/spi/spi-phytium-dma.c, drivers/spi/spi-phytium-pci.c, drivers/spi/spi-phytium-plat.c, drivers/spi/spi-phytium-qspi.c, drivers/spi/spi-phytium.c, drivers/spi/spi-phytium.h, drivers/tty/serial/phytium-uart.c, drivers/w1/masters/phytium_w1.c, include/linux/irqchip/arm-gic-phytium-2500.h, sound/pci/hda/hda_phytium.c, sound/pci/hda/hda_phytium.h, sound/soc/codecs/es8336.c, sound/soc/codecs/es8336.h, sound/soc/codecs/es8388.c, sound/soc/phytium/local.h, sound/soc/phytium/phytium_i2s.c, sound/soc/phytium/pmdk_dp.c, sound/soc/phytium/pmdk_es8336.c, and sound/soc/phytium/pmdk_es8388.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 7ff6ad4d41bbf1..ab3e98535e1e2e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -2,7 +2,7 @@ /* * Phytium DWMAC specific glue layer * - * Copyright (C) 2022, Phytium Technology Co., Ltd. + * Copyright (c) 2022-2024 Phytium Technology Co., Ltd. * * Chen Baozi */ From 9b83d12f544dbabb806a03e51114b3d96cb4b892 Mon Sep 17 00:00:00 2001 From: Zhu Honglei Date: Wed, 20 Mar 2024 14:53:55 +0800 Subject: [PATCH 305/521] BACKPORT: PHYTIUM: edac: Phytium: Add edac driver to receive RAS error Support for error detection and correction on the Phytium Pe220x family of SOCs. Signed-off-by: Zhu Honglei Signed-off-by: Lan Hengyu Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Change-Id: Iaa02a12427e8a032e12dcf148da9b6deb574a2f6 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/fcb536a33d3ad3a005af1113aef503352fba964a [Kexy: Resolved minor conflict in MAINTAINERS, dropped changes in drivers/edac/Kconfig, drivers/edac/Makefile, and drivers/edac/phytium_edac.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- MAINTAINERS | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index d5d1425952e24a..dedd131668d3e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3304,12 +3304,6 @@ S: Maintained W: http://www.digriz.org.uk/ts78xx/kernel F: arch/arm/mach-orion5x/ts78xx-* -ARM/PHYTIUM SOC SUPPORT -M: Chen Baozi -S: Maintained -W: https://www.phytium.com.cn -F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c - ARM/QUALCOMM CHROMEBOOK SUPPORT R: cros-qcom-dts-watchers@chromium.org F: arch/arm64/boot/dts/qcom/sc7180* @@ -21661,6 +21655,11 @@ F: include/sound/pxa2xx-lib.h F: sound/arm/pxa* F: sound/soc/pxa/ +ARM/PHYTIUM SOC SUPPORT +M: Wang Yinfeng +S: Maintained +W: https://www.phytium.com.cn + QAT DRIVER M: Giovanni Cabiddu L: qat-linux@intel.com From dfd05bc1735fcb631ea3951c869374b6a50ca2c5 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 25 Mar 2024 17:38:27 +0800 Subject: [PATCH 306/521] BACKPORT: PHYTIUM: net/stmmac: Add phytium DWMAC driver support v2 Modify stmmmac driver to support phytium DWMAC controler. Signed-off-by: Li Wencheng Signed-off-by: Chen Baozi Signed-off-by: Wang Yinfeng Change-Id: I878377c5fb88935dcbc7b83f368ae5408938f890 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f18feb0bef4216b257a29d176e7366c7025f3115 [Kexy: Resolved minor conflict in MAINTAINERS] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- MAINTAINERS | 1 + .../ethernet/stmicro/stmmac/dwmac-phytium.c | 37 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index dedd131668d3e1..99a988793f61b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21659,6 +21659,7 @@ ARM/PHYTIUM SOC SUPPORT M: Wang Yinfeng S: Maintained W: https://www.phytium.com.cn +F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c QAT DRIVER M: Giovanni Cabiddu diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index ab3e98535e1e2e..b7e1c422284ff2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -37,8 +37,7 @@ static int phytium_get_mac_mode(struct fwnode_handle *fwnode) static int phytium_dwmac_acpi_phy(struct plat_stmmacenet_data *plat, struct fwnode_handle *np, struct device *dev) { - plat->mdio_bus_data = - devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); + plat->mdio_bus_data = devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); if (!plat->mdio_bus_data) return -ENOMEM; @@ -60,8 +59,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) if (!plat) return -ENOMEM; - plat->dma_cfg = - devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); + plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); if (!plat->dma_cfg) return -ENOMEM; @@ -113,12 +111,10 @@ static int phytium_dwmac_probe(struct platform_device *pdev) &plat->unicast_filter_entries)) plat->unicast_filter_entries = 1; - if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", - &plat->tx_fifo_size)) + if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", &plat->tx_fifo_size)) plat->tx_fifo_size = 0x1000; - if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", - &plat->rx_fifo_size)) + if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", &plat->rx_fifo_size)) plat->rx_fifo_size = 0x1000; if (phytium_dwmac_acpi_phy(plat, fwnode, &pdev->dev)) @@ -133,11 +129,9 @@ static int phytium_dwmac_probe(struct platform_device *pdev) clk_freq = 125000000; /* Set system clock */ - snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", - plat->bus_id); + snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", plat->bus_id); - plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, - 0, clk_freq); + plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, 0, clk_freq); if (IS_ERR(plat->stmmac_clk)) { dev_warn(&pdev->dev, "Fail to register stmmac-clk\n"); plat->stmmac_clk = NULL; @@ -158,13 +152,10 @@ static int phytium_dwmac_probe(struct platform_device *pdev) fwnode_property_read_u32(fwnode, "snps,txpbl", &plat->dma_cfg->txpbl); fwnode_property_read_u32(fwnode, "snps,rxpbl", &plat->dma_cfg->rxpbl); - plat->dma_cfg->pblx8 = - !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); + plat->dma_cfg->pblx8 = !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); - plat->dma_cfg->fixed_burst = - fwnode_property_read_bool(fwnode, "snps,fixed-burst"); - plat->dma_cfg->mixed_burst = - fwnode_property_read_bool(fwnode, "snps,mixed-burst"); + plat->dma_cfg->fixed_burst = fwnode_property_read_bool(fwnode, "snps,fixed-burst"); + plat->dma_cfg->mixed_burst = fwnode_property_read_bool(fwnode, "snps,mixed-burst"); plat->axi->axi_lpi_en = false; plat->axi->axi_xit_frm = false; @@ -181,10 +172,10 @@ static int phytium_dwmac_probe(struct platform_device *pdev) } stmmac_res.wol_irq = stmmac_res.irq; - return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); + return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -void phytium_dwmac_remove(struct platform_device *pdev) +int phytium_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -192,12 +183,14 @@ void phytium_dwmac_remove(struct platform_device *pdev) stmmac_pltfr_remove(pdev); clk_unregister_fixed_rate(plat->stmmac_clk); + + return 0; } #ifdef CONFIG_OF static const struct of_device_id phytium_dwmac_of_match[] = { { .compatible = "phytium,gmac" }, - {} + { } }; MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); #endif @@ -212,7 +205,7 @@ MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); static struct platform_driver phytium_dwmac_driver = { .probe = phytium_dwmac_probe, - .remove_new = phytium_dwmac_remove, + .remove = phytium_dwmac_remove, .driver = { .name = "phytium-dwmac", .of_match_table = of_match_ptr(phytium_dwmac_of_match), From 306782abd8b296520e148e49b295ec23ea311c14 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Thu, 13 Jun 2024 17:50:28 +0800 Subject: [PATCH 307/521] PHYTIUM: net: stmmac: phytium driver add pm support Test S3 with net device open will got this cut log. ... NETDEV WATCHDOG: eth0 (phytium-dwmac): transmit queue 0 timed out 8532ms WARNING: CPU: 2 PID: 0 at net/sched/sch_generic.c:525 dev_watchdog+0x234/0x23c ... Miss pm function in phytium_dwmac_driver. So add it. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/e4084ab6219ff1e5b2ad5cc68b58be491b3f448f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index b7e1c422284ff2..7df8417da4b623 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -208,6 +208,7 @@ static struct platform_driver phytium_dwmac_driver = { .remove = phytium_dwmac_remove, .driver = { .name = "phytium-dwmac", + .pm = &stmmac_pltfr_pm_ops, .of_match_table = of_match_ptr(phytium_dwmac_of_match), .acpi_match_table = ACPI_PTR(phytium_dwmac_acpi_ids), }, From 143e20e89cb4ecd20616a102f8a439e20fd4406e Mon Sep 17 00:00:00 2001 From: Song Wenting Date: Mon, 25 Mar 2024 13:55:20 +0800 Subject: [PATCH 308/521] BACKPORT: PHYTIUM: net: phytium: Add support for phytium GMAC This patch provides support for Phytium GMAC controller driver. Signed-off-by: Song Wenting Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If526fbf5cda879b4445b027ec32b94e9403b3870 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/4a478e247258caf39ae5d7541ecb6c4aa08c90b8 [Kexy: Resolved minor conflict in MAINTAINERS] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- MAINTAINERS | 3 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/phytium/Kconfig | 58 + drivers/net/ethernet/phytium/Makefile | 17 + drivers/net/ethernet/phytium/phytmac.h | 591 +++++ .../net/ethernet/phytium/phytmac_ethtool.c | 544 ++++ drivers/net/ethernet/phytium/phytmac_main.c | 2236 +++++++++++++++++ drivers/net/ethernet/phytium/phytmac_pci.c | 318 +++ .../net/ethernet/phytium/phytmac_platform.c | 255 ++ drivers/net/ethernet/phytium/phytmac_ptp.c | 304 +++ drivers/net/ethernet/phytium/phytmac_ptp.h | 55 + drivers/net/ethernet/phytium/phytmac_v1.c | 1370 ++++++++++ drivers/net/ethernet/phytium/phytmac_v1.h | 453 ++++ drivers/net/ethernet/phytium/phytmac_v2.c | 1254 +++++++++ drivers/net/ethernet/phytium/phytmac_v2.h | 362 +++ 16 files changed, 7822 insertions(+) create mode 100644 drivers/net/ethernet/phytium/Kconfig create mode 100644 drivers/net/ethernet/phytium/Makefile create mode 100644 drivers/net/ethernet/phytium/phytmac.h create mode 100644 drivers/net/ethernet/phytium/phytmac_ethtool.c create mode 100644 drivers/net/ethernet/phytium/phytmac_main.c create mode 100644 drivers/net/ethernet/phytium/phytmac_pci.c create mode 100644 drivers/net/ethernet/phytium/phytmac_platform.c create mode 100644 drivers/net/ethernet/phytium/phytmac_ptp.c create mode 100644 drivers/net/ethernet/phytium/phytmac_ptp.h create mode 100644 drivers/net/ethernet/phytium/phytmac_v1.c create mode 100644 drivers/net/ethernet/phytium/phytmac_v1.h create mode 100644 drivers/net/ethernet/phytium/phytmac_v2.c create mode 100644 drivers/net/ethernet/phytium/phytmac_v2.h diff --git a/MAINTAINERS b/MAINTAINERS index 99a988793f61b2..74848d12afb7dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21659,6 +21659,9 @@ ARM/PHYTIUM SOC SUPPORT M: Wang Yinfeng S: Maintained W: https://www.phytium.com.cn +F: drivers/net/ethernet/Kconfig +F: drivers/net/ethernet/Makefile +F: drivers/net/ethernet/phytium/* F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c QAT DRIVER diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index b8f70e2a1763ad..637a07cd3fe1f7 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -158,6 +158,7 @@ config OA_TC6 source "drivers/net/ethernet/pasemi/Kconfig" source "drivers/net/ethernet/pensando/Kconfig" +source "drivers/net/ethernet/phytium/Kconfig" source "drivers/net/ethernet/qlogic/Kconfig" source "drivers/net/ethernet/brocade/Kconfig" source "drivers/net/ethernet/qualcomm/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 57344fec6ce047..d2590894e2228b 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_LPC_ENET) += nxp/ obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/ obj-$(CONFIG_ETHOC) += ethoc.o obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ +obj-$(CONFIG_NET_VENDOR_PHYTIUM) += phytium/ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig new file mode 100644 index 00000000000000..14a77adbfdc12f --- /dev/null +++ b/drivers/net/ethernet/phytium/Kconfig @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phytium device configuration +# + +config NET_VENDOR_PHYTIUM + bool "Phytium devices" + depends on HAS_IOMEM + default y + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + remaining Cadence network card questions. If you say Y, you will be + asked for your specific card in the following questions. + +if NET_VENDOR_PHYTIUM + +config PHYTMAC + tristate "Phytium GMAC support" + depends on HAS_DMA + select PHYLINK + select CRC32 + help + If you have a network (Ethernet) controller of this type, say Y + or M here. + + To compile this driver as a module, choose M here: the module + will be phytmac. + +config PHYTMAC_ENABLE_PTP + bool "Enable IEEE 1588 hwstamp" + depends on PHYTMAC + depends on PTP_1588_CLOCK + default y + help + Enable IEEE 1588 PTP support for PHYTMAC. + +config PHYTMAC_PLATFORM + tristate "Phytmac Platform support" + depends on PHYTMAC + help + This is Platform driver. + + To compile this driver as a module, choose M here: the module + will be called phytmac_platform. + +config PHYTMAC_PCI + tristate "Phytmac PCI support" + depends on PHYTMAC && PCI + help + This is PCI driver. + + To compile this driver as a module, choose M here: the module + will be called phytmac_pci. + +endif # NET_VENDOR_PHYTIUM diff --git a/drivers/net/ethernet/phytium/Makefile b/drivers/net/ethernet/phytium/Makefile new file mode 100644 index 00000000000000..6e710d4d54b664 --- /dev/null +++ b/drivers/net/ethernet/phytium/Makefile @@ -0,0 +1,17 @@ + +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Phytium network device drivers. +# +# + +obj-$(CONFIG_PHYTMAC) += phytmac.o + +phytmac-objs := phytmac_main.o phytmac_ethtool.o phytmac_v1.o phytmac_v2.o +phytmac-$(CONFIG_PHYTMAC_ENABLE_PTP) += phytmac_ptp.o + +obj-$(CONFIG_PHYTMAC_PLATFORM) += phytmac-platform.o +phytmac-platform-objs := phytmac_platform.o + +obj-$(CONFIG_PHYTMAC_PCI) += phytmac-pci.o +phytmac-pci-objs := phytmac_pci.o diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h new file mode 100644 index 00000000000000..b90e1551499ef7 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -0,0 +1,591 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_H +#define _PHYTMAC_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTMAC_DRV_NAME "phytium-mac" +#define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" + +#define PHYTMAC_DEFAULT_MSG_ENABLE \ + (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_INTR | \ + NETIF_MSG_HW |\ + NETIF_MSG_PKTDATA) + +#define IRQ_TYPE_INT 0 +#define IRQ_TYPE_MSI 1 +#define IRQ_TYPE_INTX 2 + +#define PHYTMAC_MAX_QUEUES 8 +#define DEFAULT_DMA_BURST_LENGTH 16 +#define DEFAULT_JUMBO_MAX_LENGTH 10240 +#define PHYTMAC_MAX_TX_LEN 16320 +#define PHYTMAC_MIN_TX_LEN 64 +#define DEFAULT_TX_RING_SIZE 512 +#define DEFAULT_RX_RING_SIZE 512 +#define MAX_TX_RING_SIZE 1024 +#define MAX_RX_RING_SIZE 4096 +#define MIN_TX_RING_SIZE 64 +#define MIN_RX_RING_SIZE 64 +#define DEFAULT_TX_DESC_MIN_FREE 64 +#define DEFAULT_RX_DESC_MIN_FREE 64 + +#define MEMORY_SIZE 4096 +#define MHU_SIZE 0x20 + +#define PHYTMAC_POWEROFF 1 +#define PHYTMAC_POWERON 2 + +#define PHYTMAC_WOL_MAGIC_PACKET 1 + +#define DEFAULT_MSG_RING_SIZE 16 + +#define PHYTMAC_CAPS_JUMBO 0x00000001 +#define PHYTMAC_CAPS_PTP 0x00000002 +#define PHYTMAC_CAPS_BD_RD_PREFETCH 0x00000004 +#define PHYTMAC_CAPS_PCS 0x00000008 +#define PHYTMAC_CAPS_LSO 0x00000010 +#define PHYTMAC_CAPS_SG_DISABLED 0x00000020 +#define PHYTMAC_CAPS_TAILPTR 0x00000040 +#define PHYTMAC_CAPS_START 0x00000080 +#define PHYTMAC_CAPS_NO_WOL 0x0000100 +#define PHYTMAC_CAPS_LPI 0x0000400 +#define PHYTMAC_CAPS_MSG 0x0000800 + +#define PHYTMAC_TX 0x1 +#define PHYTMAC_RX 0x2 + +#define PHYTMAC_GREGS_LEN 16 + +#define PHYTMAC_MTU_MIN_SIZE ETH_MIN_MTU + +#define EQUAL(a, b) ((a) == (b)) + +#define TXD_USE_COUNT(_pdata, s) DIV_ROUND_UP((s), (_pdata)->max_tx_length) + +/* Bit manipulation macros */ +#define PHYTMAC_BIT(_field) \ + (1 << PHYTMAC_##_field##_INDEX) + +#define PHYTMAC_BITS(_field, value) \ + (((value) & ((1 << PHYTMAC_##_field##_WIDTH) - 1)) \ + << PHYTMAC_##_field##_INDEX) + +#define PHYTMAC_GET_BITS(_var, _field) \ + (((_var) >> (PHYTMAC_##_field##_INDEX)) \ + & ((0x1 << (PHYTMAC_##_field##_WIDTH)) - 1)) + +#define PHYTMAC_SET_BITS(_var, _field, _val) \ + (((_var) & ~(((1 << PHYTMAC_##_field##_WIDTH) - 1) \ + << PHYTMAC_##_field##_INDEX)) \ + | (((_val) & ((1 << PHYTMAC_##_field##_WIDTH) - 1)) \ + << PHYTMAC_##_field##_INDEX)) + +#define PHYTMAC_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_READ_BITS(_pdata, _reg, _field) \ + PHYTMAC_GET_BITS(PHYTMAC_READ((_pdata), _reg), _field) + +#define PHYTMAC_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_MSG_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mac_regs + (_reg)) + +#define LSO_UFO 1 +#define LSO_TSO 2 + +#define PHYTMAC_INT_TX_COMPLETE 0x1 +#define PHYTMAC_INT_TX_ERR 0x2 +#define PHYTMAC_INT_RX_COMPLETE 0x4 +#define PHYTMAC_INT_RX_OVERRUN 0x8 +#define PHYTMAC_INT_RX_DESC_FULL 0x10 +#define PHYTMAC_RX_INT_FLAGS (PHYTMAC_INT_RX_COMPLETE) +#define PHYTMAC_TX_INT_FLAGS (PHYTMAC_INT_TX_COMPLETE \ + | PHYTMAC_INT_TX_ERR) + +#define PHYTMAC_WAKE_MAGIC 0x00000001 +#define PHYTMAC_WAKE_ARP 0x00000002 +#define PHYTMAC_WAKE_UCAST 0x00000004 +#define PHYTMAC_WAKE_MCAST 0x00000008 + +struct packet_info { + int lso; + int desc_cnt; + int hdrlen; + int nocrc; + u32 mss; + u32 seq; +}; + +#define DEV_TYPE_PLATFORM 0 +#define DEV_TYPE_PCI 1 + +struct phytmac_statistics { + char stat_string[ETH_GSTRING_LEN]; +}; + +#define STAT_TITLE(title) { \ + .stat_string = title, \ +} + +static const struct phytmac_statistics phytmac_statistics[] = { + STAT_TITLE("tx_octets"), + STAT_TITLE("tx_packets"), + STAT_TITLE("tx_bcast_packets"), + STAT_TITLE("tx_mcase_packets"), + STAT_TITLE("tx_pause_packets"), + STAT_TITLE("tx_64_byte_packets"), + STAT_TITLE("tx_65_127_byte_packets"), + STAT_TITLE("tx_128_255_byte_packets"), + STAT_TITLE("tx_256_511_byte_packets"), + STAT_TITLE("tx_512_1023_byte_packets"), + STAT_TITLE("tx_1024_1518_byte_packets"), + STAT_TITLE("tx_more_than_1518_byte_packets"), + STAT_TITLE("tx_underrun"), + STAT_TITLE("tx_single_collisions"), + STAT_TITLE("tx_multiple_collisions"), + STAT_TITLE("tx_excessive_collisions"), + STAT_TITLE("tx_late_collisions"), + STAT_TITLE("tx_deferred"), + STAT_TITLE("tx_carrier_sense_errors"), + STAT_TITLE("rx_octets"), + STAT_TITLE("rx_packets"), + STAT_TITLE("rx_bcast_packets"), + STAT_TITLE("rx_mcast_packets"), + STAT_TITLE("rx_pause_packets"), + STAT_TITLE("rx_64_byte_packets"), + STAT_TITLE("rx_65_127_byte_packets"), + STAT_TITLE("rx_128_255_byte_packets"), + STAT_TITLE("rx_256_511_byte_packets"), + STAT_TITLE("rx_512_1023_byte_packets"), + STAT_TITLE("rx_1024_1518_byte_packets"), + STAT_TITLE("rx_more_than_1518_byte_packets"), + STAT_TITLE("rx_undersized_packets"), + STAT_TITLE("rx_oversize_packets"), + STAT_TITLE("rx_jabbers"), + STAT_TITLE("rx_fcs_errors"), + STAT_TITLE("rx_length_errors"), + STAT_TITLE("rx_symbol_errors"), + STAT_TITLE("rx_alignment_errors"), + STAT_TITLE("rx_resource_over"), + STAT_TITLE("rx_overruns"), + STAT_TITLE("rx_iphdr_csum_errors"), + STAT_TITLE("rx_tcp_csum_errors"), + STAT_TITLE("rx_udp_csum_errors"), +}; + +#define PHYTMAC_STATS_LEN ARRAY_SIZE(phytmac_statistics) + +/* per queue statistics, each should be unsigned long type */ +struct phytmac_queue_stats { + unsigned long rx_packets; + unsigned long rx_bytes; + unsigned long rx_dropped; + unsigned long tx_packets; + unsigned long tx_bytes; + unsigned long tx_dropped; +}; + +static const struct phytmac_statistics queue_statistics[] = { + STAT_TITLE("rx_packets"), + STAT_TITLE("rx_bytes"), + STAT_TITLE("rx_dropped"), + STAT_TITLE("tx_packets"), + STAT_TITLE("tx_bytes"), + STAT_TITLE("tx_dropped"), +}; + +#define QUEUE_STATS_LEN ARRAY_SIZE(queue_statistics) + +struct phytmac_config { + struct phytmac_hw_if *hw_if; + u32 caps; + u32 tsu_rate; + u16 queue_num; +}; + +struct phytmac_stats { + u64 tx_octets; + u64 tx_packets; + u64 tx_bcast_packets; + u64 tx_mcase_packets; + u64 tx_pause_packets; + u64 tx_64_byte_packets; + u64 tx_65_127_byte_packets; + u64 tx_128_255_byte_packets; + u64 tx_256_511_byte_packets; + u64 tx_512_1023_byte_packets; + u64 tx_1024_1518_byte_packets; + u64 tx_more_than_1518_byte_packets; + u64 tx_underrun; + u64 tx_single_collisions; + u64 tx_multiple_collisions; + u64 tx_excessive_collisions; + u64 tx_late_collisions; + u64 tx_deferred; + u64 tx_carrier_sense_errors; + u64 rx_octets; + u64 rx_packets; + u64 rx_bcast_packets; + u64 rx_mcast_packets; + u64 rx_pause_packets; + u64 rx_64_byte_packets; + u64 rx_65_127_byte_packets; + u64 rx_128_255_byte_packets; + u64 rx_256_511_byte_packets; + u64 rx_512_1023_byte_packets; + u64 rx_1024_1518_byte_packets; + u64 rx_more_than_1518_byte_packets; + u64 rx_undersized_packets; + u64 rx_oversize_packets; + u64 rx_jabbers; + u64 rx_fcs_errors; + u64 rx_length_errors; + u64 rx_symbol_errors; + u64 rx_alignment_errors; + u64 rx_resource_over; + u64 rx_overruns; + u64 rx_iphdr_csum_errors; + u64 rx_tcp_csum_errors; + u64 rx_udp_csum_errors; +}; + +struct ts_incr { + u32 sub_ns; + u32 ns; +}; + +enum phytmac_bd_control { + TS_DISABLED, + TS_FRAME_PTP_EVENT_ONLY, + TS_ALL_PTP_FRAMES, + TS_ALL_FRAMES, +}; + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP +struct phytmac_dma_desc { + u32 desc0; + u32 desc1; + u32 desc2; + u32 desc3; + u32 desc4; + u32 desc5; +}; +#else +struct phytmac_dma_desc { + u32 desc0; + u32 desc1; + u32 desc2; + u32 desc3; +}; +#endif + +struct phytmac_tx_skb { + struct sk_buff *skb; + dma_addr_t addr; + size_t length; + bool mapped_as_page; +}; + +struct phytmac_tx_ts { + struct sk_buff *skb; + u32 ts_1; + u32 ts_2; +}; + +struct phytmac_queue { + struct phytmac *pdata; + int irq; + int index; + + /* tx queue info */ + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_xmit_more; + dma_addr_t tx_ring_addr; + struct work_struct tx_error_task; + struct napi_struct tx_napi; + struct phytmac_dma_desc *tx_ring; + struct phytmac_tx_skb *tx_skb; + /* Lock to protect tx */ + spinlock_t tx_lock; + + /* rx queue info */ + dma_addr_t rx_ring_addr; + unsigned int rx_head; + unsigned int rx_tail; + struct phytmac_dma_desc *rx_ring; + struct sk_buff **rx_skb; + struct napi_struct rx_napi; + struct phytmac_queue_stats stats; + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP + struct work_struct tx_ts_task; + unsigned int tx_ts_head; + unsigned int tx_ts_tail; + struct phytmac_tx_ts tx_timestamps[128]; +#endif +}; + +struct ethtool_rx_fs_item { + struct ethtool_rx_flow_spec fs; + struct list_head list; +}; + +struct ethtool_rx_fs_list { + struct list_head list; + unsigned int count; +}; + +struct phytmac_msg { + struct completion tx_msg_comp; + u32 tx_msg_ring_size; + u32 rx_msg_ring_size; + u32 tx_msg_head; + u32 tx_msg_tail; + u32 rx_msg_head; + u32 rx_msg_tail; + /* Lock to protect msg */ + spinlock_t msg_lock; +}; + +struct ts_ctrl { + int tx_control; + int rx_control; + int one_step; +}; + +struct phytmac { + void __iomem *mac_regs; + void __iomem *msg_regs; + void __iomem *mhu_regs; + struct pci_dev *pcidev; + struct platform_device *platdev; + struct net_device *ndev; + struct device *dev; + struct ncsi_dev *ncsidev; + struct fwnode_handle *fwnode; + struct phytmac_hw_if *hw_if; + struct phytmac_msg msg_ring; + int dev_type; + int sfp_irq; + int irq_type; + int queue_irq[PHYTMAC_MAX_QUEUES]; + u32 rx_irq_mask; + u32 tx_irq_mask; + u32 msg_enable; + u32 capacities; + u32 max_tx_length; + u32 min_tx_length; + u32 jumbo_len; + u32 wol; + u32 lpi; + u32 power_state; + struct work_struct restart_task; + /* Lock to protect mac config */ + spinlock_t lock; + /* Lock to protect msg tx */ + spinlock_t msg_lock; + u32 rx_ring_size; + u32 tx_ring_size; + u32 dma_data_width; + u32 dma_addr_width; + u32 dma_burst_length; + int rx_bd_prefetch; + int tx_bd_prefetch; + int rx_buffer_len; + u16 queues_max_num; + u16 queues_num; + struct phytmac_queue queues[PHYTMAC_MAX_QUEUES]; + struct phytmac_stats stats; + u64 ethtool_stats[PHYTMAC_STATS_LEN + + QUEUE_STATS_LEN * PHYTMAC_MAX_QUEUES]; + int use_ncsi; + int use_mii; + struct mii_bus *mii_bus; + struct phylink *phylink; + struct phylink_config phylink_config; + struct phylink_pcs phylink_pcs; + int pause; + phy_interface_t phy_interface; + int speed; + int duplex; + int autoneg; + /* 1588 */ + spinlock_t ts_clk_lock; /* clock locking */ + unsigned int ts_rate; + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + struct ts_incr ts_incr; + struct hwtstamp_config ts_config; + struct ts_ctrl ts_ctrl; + /* RX queue filer rule set */ + struct ethtool_rx_fs_list rx_fs_list; + /* Lock to protect fs */ + spinlock_t rx_fs_lock; + unsigned int max_rx_fs; +}; + +struct phytmac_hw_if { + int (*init_msg_ring)(struct phytmac *pdata); + int (*init_hw)(struct phytmac *pdata); + void (*reset_hw)(struct phytmac *pdata); + int (*init_ring_hw)(struct phytmac *pdata); + int (*poweron)(struct phytmac *pdata, int on); + int (*set_wol)(struct phytmac *pdata, int wake); + int (*get_feature)(struct phytmac *pdata); + int (*set_mac_address)(struct phytmac *pdata, const u8 *addr); + int (*get_mac_address)(struct phytmac *pdata, u8 *addr); + int (*enable_promise)(struct phytmac *pdata, int enable); + int (*enable_multicast)(struct phytmac *pdata, int enable); + int (*set_hash_table)(struct phytmac *pdata, unsigned long *mc_filter); + int (*enable_rx_csum)(struct phytmac *pdata, int enable); + int (*enable_tx_csum)(struct phytmac *pdata, int enable); + int (*enable_pause)(struct phytmac *pdata, int enable); + int (*enable_autoneg)(struct phytmac *pdata, int enable); + int (*enable_network)(struct phytmac *pdata, int enable, int rx_tx); + void (*get_stats)(struct phytmac *pdata); + void (*get_regs)(struct phytmac *pdata, u32 *reg_buff); + int (*set_speed)(struct phytmac *pdata); + + void (*mac_config)(struct phytmac *pdata, u32 mode, + const struct phylink_link_state *state); + int (*mac_linkup)(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex); + int (*mac_linkdown)(struct phytmac *pdata); + int (*pcs_linkup)(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex); + int (*pcs_linkdown)(struct phytmac *pdata); + unsigned int (*get_link)(struct phytmac *pdata, phy_interface_t interface); + + /* For RX coalescing */ + int (*config_rx_coalesce)(struct phytmac *pdata); + int (*config_tx_coalesce)(struct phytmac *pdata); + + /* ethtool nfc */ + int (*add_fdir_entry)(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow); + int (*del_fdir_entry)(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow); + + /* mido ops */ + int (*enable_mdio_control)(struct phytmac *pdata, int enable); + int (*mdio_read)(struct phytmac *pdata, int mii_id, int regnum); + int (*mdio_write)(struct phytmac *pdata, int mii_id, + int regnum, u16 data); + int (*mdio_read_c45)(struct phytmac *pdata, int mii_id, int devad, int regnum); + int (*mdio_write_c45)(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data); + void (*enable_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*disable_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*clear_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*mask_irq)(struct phytmac *pdata, int queue_index, u32 mask); + unsigned int (*get_irq)(struct phytmac *pdata, int queue_index); + unsigned int (*get_intx_mask)(struct phytmac *pdata); + unsigned int (*tx_map)(struct phytmac_queue *pdata, u32 tx_tail, + struct packet_info *packet); + void (*init_rx_map)(struct phytmac_queue *queue, u32 index); + unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); + unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); + void (*transmit)(struct phytmac_queue *queue); + void (*restart)(struct phytmac *pdata); + int (*tx_complete)(const struct phytmac_dma_desc *desc); + int (*rx_complete)(const struct phytmac_dma_desc *desc); + int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); + dma_addr_t (*get_desc_addr)(const struct phytmac_dma_desc *desc); + bool (*rx_checksum)(const struct phytmac_dma_desc *desc); + void (*set_desc_rxused)(struct phytmac_dma_desc *desc); + bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); + bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); + bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); + void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); + void (*clear_tx_desc)(struct phytmac_queue *queue); + /* ptp */ + void (*init_ts_hw)(struct phytmac *pdata); + void (*get_time)(struct phytmac *pdata, struct timespec64 *ts); + void (*set_time)(struct phytmac *pdata, time64_t sec, long nsec); + int (*set_ts_config)(struct phytmac *pdata, struct ts_ctrl *ctrl); + void (*clear_time)(struct phytmac *pdata); + int (*set_incr)(struct phytmac *pdata, struct ts_incr *incr); + int (*adjust_fine)(struct phytmac *pdata, long ppm, bool negative); + int (*adjust_time)(struct phytmac *pdata, s64 delta, int neg); + int (*ts_valid)(struct phytmac *pdata, struct phytmac_dma_desc *desc, + int direction); + void (*get_timestamp)(struct phytmac *pdata, u32 ts_1, u32 ts_2, + struct timespec64 *ts); + unsigned int (*get_ts_rate)(struct phytmac *pdata); +}; + +/* mhu */ +#define PHYTMAC_MHU_AP_CPP_STAT 0x00 +#define PHYTMAC_MHU_AP_CPP_SET 0x04 +#define PHYTMAC_MHU_CPP_DATA0 0x18 +#define PHYTMAC_MHU_CPP_DATA1 0x1c + +#define PHYTMAC_MHU_STAT_BUSY_INDEX 0 +#define PHYTMAC_MHU_STAT_BUSY_WIDTH 1 + +#define PHYTMAC_MHU_SET_INDEX 0 +#define PHYTMAC_MHU_SET_WIDTH 1 + +#define PHYTMAC_DATA0_FREE_INDEX 0 +#define PHYTMAC_DATA0_FREE_WIDTH 1 +#define PHYTMAC_DATA0_DOMAIN_INDEX 1 +#define PHYTMAC_DATA0_DOMAIN_WIDTH 7 +#define PHYTMAC_DATA0_MSG_INDEX 8 +#define PHYTMAC_DATA0_MSG_WIDTH 8 +#define PHYTMAC_MSG_PM 0x04 +#define PHYTMAC_DATA0_PRO_INDEX 16 +#define PHYTMAC_DATA0_PRO_WIDTH 8 +#define PHYTMAC_PRO_ID 0x11 +#define PHYTMAC_DATA0_PAYLOAD_INDEX 24 +#define PHYTMAC_DATA0_PAYLOAD_WIDTH 8 + +#define PHYTMAC_DATA1_STAT_INDEX 0 +#define PHYTMAC_DATA1_STAT_WIDTH 28 +#define PHYTMAC_STATON 8 +#define PHYTMAC_STATOFF 0 +#define PHYTMAC_DATA1_MUST0_INDEX 28 +#define PHYTMAC_DATA1_MUST0_WIDTH 2 +#define PHYTMAC_DATA1_STATTYPE_INDEX 30 +#define PHYTMAC_DATA1_STATTYPE_WIDTH 1 +#define PHYTMAC_STATTYPE 0x1 +#define PHYTMAC_DATA1_MUST1_INDEX 31 +#define PHYTMAC_DATA1_MUST1_WIDTH 1 + +#define PHYTMAC_MHU_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mhu_regs + (_reg)) +#define PHYTMAC_MHU_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mhu_regs + (_reg)) +#define PHYTMAC_READ_STAT(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_AP_CPP_STAT) +#define PHYTMAC_READ_DATA0(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA0) +#define PHYTMAC_TIMEOUT 1000000000 /* in usecs */ + +struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, + unsigned int index); +inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, + unsigned int index); +inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, + unsigned int index); +void phytmac_set_ethtool_ops(struct net_device *netdev); +int phytmac_drv_probe(struct phytmac *pdata); +int phytmac_drv_remove(struct phytmac *pdata); +int phytmac_drv_suspend(struct phytmac *pdata); +int phytmac_drv_resume(struct phytmac *pdata); +struct phytmac *phytmac_alloc_pdata(struct device *dev); +void phytmac_free_pdata(struct phytmac *pdata); +int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c new file mode 100644 index 00000000000000..592d2d9dc6d4b2 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" +#include "phytmac_ptp.h" + +static void phytmac_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, + u64 *data) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + int i = PHYTMAC_STATS_LEN, j; + int q; + struct phytmac_queue *queue; + unsigned long *stat; + + hw_if->get_stats(pdata); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) + for (j = 0, stat = &queue->stats.rx_packets; j < QUEUE_STATS_LEN; ++j, ++stat) + pdata->ethtool_stats[i++] = *stat; + + memcpy(data, &pdata->ethtool_stats, sizeof(u64) + * (PHYTMAC_STATS_LEN + QUEUE_STATS_LEN * pdata->queues_num)); +} + +static inline int phytmac_get_sset_count(struct net_device *ndev, int sset) +{ + struct phytmac *pdata = netdev_priv(ndev); + + switch (sset) { + case ETH_SS_STATS: + return PHYTMAC_STATS_LEN + QUEUE_STATS_LEN * pdata->queues_num; + default: + return -EOPNOTSUPP; + } +} + +static void phytmac_get_ethtool_strings(struct net_device *ndev, u32 sset, u8 *p) +{ + char stat_string[ETH_GSTRING_LEN]; + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + unsigned int i; + unsigned int q; + + switch (sset) { + case ETH_SS_STATS: + for (i = 0; i < PHYTMAC_STATS_LEN; i++, p += ETH_GSTRING_LEN) + memcpy(p, phytmac_statistics[i].stat_string, + ETH_GSTRING_LEN); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + for (i = 0; i < QUEUE_STATS_LEN; i++, p += ETH_GSTRING_LEN) { + snprintf(stat_string, ETH_GSTRING_LEN, "q%d_%s", + q, queue_statistics[i].stat_string); + memcpy(p, stat_string, ETH_GSTRING_LEN); + } + } + break; + } +} + +static inline int phytmac_get_regs_len(struct net_device *ndev) +{ + return PHYTMAC_GREGS_LEN; +} + +static void phytmac_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, + void *p) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 *regs_buff = p; + + memset(p, 0, PHYTMAC_GREGS_LEN * sizeof(u32)); + + hw_if->get_regs(pdata, regs_buff); +} + +static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct phytmac *pdata = netdev_priv(ndev); + + phylink_ethtool_get_wol(pdata->phylink, wol); + + if (pdata->wol & PHYTMAC_WAKE_MAGIC) { + wol->wolopts |= WAKE_MAGIC; + wol->supported |= WAKE_MAGIC; + } + if (pdata->wol & PHYTMAC_WAKE_ARP) { + wol->wolopts |= WAKE_ARP; + wol->supported |= WAKE_ARP; + } + if (pdata->wol & PHYTMAC_WAKE_UCAST) { + wol->wolopts |= WAKE_UCAST; + wol->supported |= WAKE_UCAST; + } + if (pdata->wol & PHYTMAC_WAKE_MCAST) { + wol->wolopts |= WAKE_MCAST; + wol->supported |= WAKE_MCAST; + } +} + +static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct phytmac *pdata = netdev_priv(ndev); + int ret; + + ret = phylink_ethtool_set_wol(pdata->phylink, wol); + + if (!ret || ret != -EOPNOTSUPP) + return ret; + + pdata->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + pdata->wol |= PHYTMAC_WAKE_MAGIC; + if (wol->wolopts & WAKE_ARP) + pdata->wol |= PHYTMAC_WAKE_ARP; + if (wol->wolopts & WAKE_UCAST) + pdata->wol |= PHYTMAC_WAKE_UCAST; + if (wol->wolopts & WAKE_MCAST) + pdata->wol |= PHYTMAC_WAKE_MCAST; + + device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + + return 0; +} + +static void phytmac_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct phytmac *pdata = netdev_priv(ndev); + + ring->rx_max_pending = MAX_RX_RING_SIZE; + ring->tx_max_pending = MAX_TX_RING_SIZE; + + ring->rx_pending = pdata->rx_ring_size; + ring->tx_pending = pdata->tx_ring_size; +} + +static int phytmac_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct phytmac *pdata = netdev_priv(ndev); + u32 new_rx_size, new_tx_size; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + new_rx_size = clamp_t(u32, ring->rx_pending, + MIN_RX_RING_SIZE, MAX_RX_RING_SIZE); + new_rx_size = roundup_pow_of_two(new_rx_size); + + new_tx_size = clamp_t(u32, ring->tx_pending, + MIN_TX_RING_SIZE, MAX_TX_RING_SIZE); + new_tx_size = roundup_pow_of_two(new_tx_size); + + if (EQUAL(new_tx_size, pdata->tx_ring_size) && + EQUAL(new_rx_size, pdata->rx_ring_size)) { + /* nothing to do */ + return 0; + } + + return phytmac_reset_ringsize(pdata, new_rx_size, new_tx_size); +} + +static int phytmac_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct phytmac *pdata = netdev_priv(ndev); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = + (1 << HWTSTAMP_TX_ONESTEP_SYNC) | + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL); + + info->phc_index = pdata->ptp_clock ? ptp_clock_index(pdata->ptp_clock) : -1; + + return 0; + } + + return ethtool_op_get_ts_info(ndev, info); +} + +static int phytmac_add_fdir_ethtool(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_flow_spec *fs = &cmd->fs; + struct ethtool_rx_fs_item *item, *newfs; + unsigned long flags; + int ret = -EINVAL; + bool added = false; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (cmd->fs.location >= pdata->max_rx_fs || + cmd->fs.ring_cookie >= pdata->queues_num) { + return -EINVAL; + } + + newfs = kmalloc(sizeof(*newfs), GFP_KERNEL); + if (!newfs) + return -ENOMEM; + memcpy(&newfs->fs, fs, sizeof(newfs->fs)); + + netdev_dbg(ndev, "Adding flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), htons(fs->h_u.tcp_ip4_spec.pdst)); + + spin_lock_irqsave(&pdata->rx_fs_lock, flags); + + /* find correct place to add in list */ + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location > newfs->fs.location) { + list_add_tail(&newfs->list, &item->list); + added = true; + break; + } else if (item->fs.location == fs->location) { + netdev_err(ndev, "Rule not added: location %d not free!\n", + fs->location); + ret = -EBUSY; + goto err; + } + } + if (!added) + list_add_tail(&newfs->list, &pdata->rx_fs_list.list); + + hw_if->add_fdir_entry(pdata, fs); + pdata->rx_fs_list.count++; + + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + return 0; + +err: + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + kfree(newfs); + return ret; +} + +static int phytmac_del_fdir_ethtool(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + struct ethtool_rx_flow_spec *fs; + unsigned long flags; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + spin_lock_irqsave(&pdata->rx_fs_lock, flags); + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + /* disable screener regs for the flow entry */ + fs = &item->fs; + netdev_dbg(ndev, "Deleting flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), + htons(fs->h_u.tcp_ip4_spec.pdst)); + + hw_if->del_fdir_entry(pdata, fs); + + list_del(&item->list); + pdata->rx_fs_list.count--; + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + kfree(item); + return 0; + } + } + + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + return -EINVAL; +} + +static int phytmac_get_fdir_entry(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + memcpy(&cmd->fs, &item->fs, sizeof(cmd->fs)); + return 0; + } + } + return -EINVAL; +} + +static int phytmac_get_all_fdir_entries(struct net_device *ndev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + u32 cnt = 0; + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + rule_locs[cnt] = item->fs.location; + cnt++; + } + cmd->data = pdata->max_rx_fs; + cmd->rule_cnt = cnt; + + return 0; +} + +static int phytmac_get_rxnfc(struct net_device *ndev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct phytmac *pdata = netdev_priv(ndev); + int ret = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = pdata->queues_num; + break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = pdata->rx_fs_list.count; + break; + case ETHTOOL_GRXCLSRULE: + ret = phytmac_get_fdir_entry(ndev, cmd); + break; + case ETHTOOL_GRXCLSRLALL: + ret = phytmac_get_all_fdir_entries(ndev, cmd, rule_locs); + break; + default: + netdev_err(ndev, "Command parameter %d is not supported\n", cmd->cmd); + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int phytmac_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *cmd) +{ + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + return phytmac_add_fdir_ethtool(ndev, cmd); + case ETHTOOL_SRXCLSRLDEL: + return phytmac_del_fdir_ethtool(ndev, cmd); + default: + netdev_err(ndev, "Command parameter %d is not supported\n", cmd->cmd); + return -EOPNOTSUPP; + } +} + +static int phytmac_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *kset) +{ + int ret = 0; + struct phytmac *pdata = netdev_priv(ndev); + u32 supported = 0; + u32 advertising = 0; + + if (!ndev->phydev) { + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + supported = SUPPORTED_10000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_10000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_10000; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + supported = SUPPORTED_2500baseX_Full | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_2500; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE + | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE + | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_100; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_1000; + } + + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.advertising, + advertising); + } else { + phy_ethtool_get_link_ksettings(ndev, kset); + } + + return ret; +} + +static int phytmac_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *kset) +{ + int ret = 0; + + if (!ndev->phydev) { + netdev_err(ndev, "fixed link interface not supported set link\n"); + ret = -EOPNOTSUPP; + } else { + phy_ethtool_set_link_ksettings(ndev, kset); + } + + return ret; +} + +static inline void phytmac_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct phytmac *pdata = netdev_priv(ndev); + + pause->rx_pause = pdata->pause; + pause->tx_pause = pdata->pause; +} + +static int phytmac_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pause->rx_pause != pdata->pause) + hw_if->enable_pause(pdata, pause->rx_pause); + + pdata->pause = pause->rx_pause; + + return 0; +} + +static inline void phytmac_get_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct phytmac *pdata = netdev_priv(ndev); + + ch->max_combined = pdata->queues_max_num; + ch->combined_count = pdata->queues_num; +} + +static int phytmac_set_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct phytmac *pdata = netdev_priv(ndev); + + if (netif_running(ndev)) + return -EBUSY; + + if (ch->combined_count > pdata->queues_max_num) { + netdev_err(ndev, "combined channel count cannot exceed %u\n", + ch->combined_count); + + return -EINVAL; + } + + pdata->queues_num = ch->combined_count; + + return 0; +} + +static inline u32 phytmac_get_msglevel(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + + return pdata->msg_enable; +} + +static inline void phytmac_set_msglevel(struct net_device *ndev, u32 level) +{ + struct phytmac *pdata = netdev_priv(ndev); + + pdata->msg_enable = level; +} + +static const struct ethtool_ops phytmac_ethtool_ops = { + .get_regs_len = phytmac_get_regs_len, + .get_regs = phytmac_get_regs, + .get_msglevel = phytmac_get_msglevel, + .set_msglevel = phytmac_set_msglevel, + .get_link = ethtool_op_get_link, + .get_ts_info = phytmac_get_ts_info, + .get_ethtool_stats = phytmac_get_ethtool_stats, + .get_strings = phytmac_get_ethtool_strings, + .get_sset_count = phytmac_get_sset_count, + .get_link_ksettings = phytmac_get_link_ksettings, + .set_link_ksettings = phytmac_set_link_ksettings, + .get_ringparam = phytmac_get_ringparam, + .set_ringparam = phytmac_set_ringparam, + .get_rxnfc = phytmac_get_rxnfc, + .set_rxnfc = phytmac_set_rxnfc, + .get_pauseparam = phytmac_get_pauseparam, + .set_pauseparam = phytmac_set_pauseparam, + .get_channels = phytmac_get_channels, + .set_channels = phytmac_set_channels, + .get_wol = phytmac_get_wol, + .set_wol = phytmac_set_wol, +}; + +void phytmac_set_ethtool_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &phytmac_ethtool_ops; +} + diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c new file mode 100644 index 00000000000000..b0977f5cae755c --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -0,0 +1,2236 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium Ethernet Controller driver + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_ptp.h" + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +#define RX_BUFFER_MULTIPLE 64 /* bytes */ +#define MAX_MTU 3072 +#define RING_ADDR_INTERVAL 128 + +#define RX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc) \ + * (pdata)->rx_ring_size) + +#define TX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc)\ + * (pdata)->tx_ring_size) + +/* Max length of transmit frame must be a multiple of 8 bytes */ +#define PHYTMAC_TX_LEN_ALIGN 8 +/* Limit maximum TX length as per Cadence TSO errata. This is to avoid a + * false amba_error in TX path from the DMA assuming there is not enough + * space in the SRAM (16KB) even when there is. + */ + +static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) +{ + if (netif_running(ndev)) + return -EBUSY; + + if (new_mtu > MAX_MTU) { + netdev_info(ndev, "Can not set MTU over %d.\n", MAX_MTU); + return -EINVAL; + } + + ndev->mtu = new_mtu; + + return 0; +} + +static int phytmac_set_mac_address(struct net_device *netdev, void *addr) +{ + struct phytmac *pdata = netdev_priv(netdev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sockaddr *saddr = addr; + + if (netif_msg_drv(pdata)) + netdev_info(netdev, "phytmac set mac address"); + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, saddr->sa_data); + + hw_if->set_mac_address(pdata, saddr->sa_data); + + return 0; +} + +static int phytmac_get_mac_address(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + u8 addr[6]; + + hw_if->get_mac_address(pdata, addr); + + if (is_valid_ether_addr(addr)) { + eth_hw_addr_set(pdata->ndev, addr); + return 0; + } + dev_info(pdata->dev, "invalid hw address, using random\n"); + eth_hw_addr_random(pdata->ndev); + + return 0; +} + +static int phytmac_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int data; + + data = hw_if->mdio_read(pdata, mii_id, regnum); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio read c22 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return data; +} + +static int phytmac_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum, u16 data) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + ret = hw_if->mdio_write(pdata, mii_id, regnum, data); + if (ret) + netdev_err(pdata->ndev, "mdio %d, reg %x, data %x write failed!\n", + mii_id, regnum, data); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio write c22 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return 0; +} + +static int phytmac_mdio_read_c45(struct mii_bus *bus, int mii_id, int devad, int regnum) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int data; + + data = hw_if->mdio_read_c45(pdata, mii_id, devad, regnum); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio read c45 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return data; +} + +static int phytmac_mdio_write_c45(struct mii_bus *bus, int mii_id, int devad, int regnum, u16 data) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + ret = hw_if->mdio_write_c45(pdata, mii_id, devad, regnum, data); + if (ret) + netdev_err(pdata->ndev, "mdio %d, reg %x, data %x write failed!\n", + mii_id, regnum, data); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio write c45 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return 0; +} + +static inline int hash_bit_value(int bitnr, __u8 *addr) +{ + if (addr[bitnr / 8] & (1 << (bitnr % 8))) + return 1; + return 0; +} + +/* Return the hash index value for the specified address. */ +static int phytmac_get_hash_index(__u8 *addr) +{ + int i, j, bitval; + int hash_index = 0; + + for (j = 0; j < 6; j++) { + for (i = 0, bitval = 0; i < 8; i++) + bitval ^= hash_bit_value(i * 6 + j, addr); + + hash_index |= (bitval << j); + } + + return hash_index; +} + +static void phytmac_set_mac_hash_table(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct netdev_hw_addr *ha; + unsigned long mc_filter[2]; + unsigned int bitnr; + + mc_filter[0] = 0; + mc_filter[1] = 0; + + netdev_for_each_mc_addr(ha, ndev) { + bitnr = phytmac_get_hash_index(ha->addr); + mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); + } + + hw_if->set_hash_table(pdata, mc_filter); +} + +/* Enable/Disable promiscuous and multicast modes. */ +static void phytmac_set_rx_mode(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + hw_if->enable_promise(pdata, ndev->flags & IFF_PROMISC); + + hw_if->enable_multicast(pdata, ndev->flags & IFF_ALLMULTI); + if (!netdev_mc_empty(ndev)) + phytmac_set_mac_hash_table(ndev); +} + +static struct net_device_stats *phytmac_get_stats(struct net_device *dev) +{ + struct phytmac *pdata = netdev_priv(dev); + struct net_device_stats *nstat = &pdata->ndev->stats; + struct phytmac_stats *stat = &pdata->stats; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pdata->power_state == PHYTMAC_POWEROFF) + return nstat; + + hw_if->get_stats(pdata); + + nstat->rx_errors = (stat->rx_fcs_errors + + stat->rx_alignment_errors + + stat->rx_overruns + + stat->rx_oversize_packets + + stat->rx_jabbers + + stat->rx_undersized_packets + + stat->rx_length_errors); + nstat->rx_dropped = stat->rx_resource_over; + nstat->tx_errors = (stat->tx_late_collisions + + stat->tx_excessive_collisions + + stat->tx_underrun + + stat->tx_carrier_sense_errors); + nstat->multicast = stat->rx_mcast_packets; + nstat->collisions = (stat->tx_single_collisions + + stat->tx_multiple_collisions + + stat->tx_excessive_collisions + + stat->tx_late_collisions); + nstat->rx_length_errors = (stat->rx_oversize_packets + + stat->rx_jabbers + + stat->rx_undersized_packets + + stat->rx_length_errors); + nstat->rx_over_errors = stat->rx_resource_over; + nstat->rx_crc_errors = stat->rx_fcs_errors; + nstat->rx_frame_errors = stat->rx_alignment_errors; + nstat->rx_fifo_errors = stat->rx_overruns; + nstat->tx_aborted_errors = stat->tx_excessive_collisions; + nstat->tx_carrier_errors = stat->tx_carrier_sense_errors; + nstat->tx_fifo_errors = stat->tx_underrun; + + return nstat; +} + +static int phytmac_calc_rx_buf_len(struct phytmac *pdata, u32 mtu) +{ + unsigned int size = mtu + ETH_HLEN + ETH_FCS_LEN; + int rx_buf_len = roundup(size, RX_BUFFER_MULTIPLE); + + netdev_dbg(pdata->ndev, "mtu [%u] rx_buffer_size [%u]\n", + mtu, rx_buf_len); + + return rx_buf_len; +} + +inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->rx_ring[index & (queue->pdata->rx_ring_size - 1)]; +} + +struct sk_buff *phytmac_get_rx_skb(struct phytmac_queue *queue, + unsigned int index) +{ + return queue->rx_skb[index & (queue->pdata->rx_ring_size - 1)]; +} + +struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->tx_skb[index & (queue->pdata->tx_ring_size - 1)]; +} + +inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->tx_ring[index & (queue->pdata->tx_ring_size - 1)]; +} + +static int phytmac_free_tx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct phytmac_dma_desc *tx_ring_base = NULL; + dma_addr_t tx_ring_base_addr; + unsigned int q; + int size; + + queue = pdata->queues; + if (queue->tx_ring) { + tx_ring_base = queue->tx_ring; + tx_ring_base_addr = queue->tx_ring_addr; + } + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + kfree(queue->tx_skb); + queue->tx_skb = NULL; + + if (queue->tx_ring) + queue->tx_ring = NULL; + } + + if (tx_ring_base) { + size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(pdata->dev, size, tx_ring_base, tx_ring_base_addr); + } + + return 0; +} + +static int phytmac_free_rx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *rx_ring_base = NULL; + dma_addr_t rx_ring_base_addr; + dma_addr_t addr; + unsigned int q; + int size, i; + + queue = pdata->queues; + if (queue->rx_ring) { + rx_ring_base = queue->rx_ring; + rx_ring_base_addr = queue->rx_ring_addr; + } + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + if (queue->rx_skb) { + for (i = 0; i < pdata->rx_ring_size; i++) { + skb = phytmac_get_rx_skb(queue, i); + if (skb) { + desc = &queue->rx_ring[i]; + addr = hw_if->get_desc_addr(desc); + dma_unmap_single(pdata->dev, addr, pdata->rx_buffer_len, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + skb = NULL; + } + } + + kfree(queue->rx_skb); + queue->rx_skb = NULL; + } + + if (queue->rx_ring) + queue->rx_ring = NULL; + } + + if (rx_ring_base) { + size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(pdata->dev, size, rx_ring_base, rx_ring_base_addr); + } + + return 0; +} + +static int phytmac_alloc_tx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct phytmac_dma_desc *tx_ring_base; + dma_addr_t tx_ring_base_addr; + unsigned int q; + int size; + + size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + + RING_ADDR_INTERVAL); + tx_ring_base = dma_alloc_coherent(pdata->dev, size, + &tx_ring_base_addr, GFP_KERNEL); + if (!tx_ring_base) + goto err; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + size = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + queue->tx_ring = (void *)tx_ring_base + q * size; + queue->tx_ring_addr = tx_ring_base_addr + q * size; + if (!queue->tx_ring) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated TX ring for queue %u of %d bytes at %08lx\n", + q, size, (unsigned long)queue->tx_ring_addr); + + size = pdata->tx_ring_size * sizeof(struct phytmac_tx_skb); + queue->tx_skb = kzalloc(size, GFP_KERNEL); + if (!queue->tx_skb) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated %d TX struct tx_skb entries at %p\n", + pdata->tx_ring_size, queue->tx_skb); + } + tx_ring_base = NULL; + + return 0; +err: + phytmac_free_tx_resource(pdata); + + return -ENOMEM; +} + +static int phytmac_alloc_rx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *rx_ring_base; + dma_addr_t rx_ring_base_addr; + unsigned int q; + int size; + int i; + + size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + + RING_ADDR_INTERVAL); + rx_ring_base = dma_alloc_coherent(pdata->dev, size, + &rx_ring_base_addr, GFP_KERNEL); + if (!rx_ring_base) + goto err; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + size = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + queue->rx_ring = (void *)rx_ring_base + q * size; + queue->rx_ring_addr = rx_ring_base_addr + q * size; + if (!queue->rx_ring) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated RX ring for queue %u of %d bytes at %08lx\n", + q, size, (unsigned long)queue->rx_ring_addr); + + for (i = 0; i < pdata->rx_ring_size; i++) + hw_if->init_rx_map(queue, i); + + size = pdata->rx_ring_size * sizeof(struct sk_buff *); + queue->rx_skb = kzalloc(size, GFP_KERNEL); + if (!queue->rx_skb) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated %d RX struct sk_buff entries at %p\n", + pdata->rx_ring_size, queue->rx_skb); + } + rx_ring_base = NULL; + + return 0; +err: + phytmac_free_rx_resource(pdata); + + return -ENOMEM; +} + +static int phytmac_alloc_resource(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + int ret; + + pdata->rx_buffer_len = phytmac_calc_rx_buf_len(pdata, ndev->mtu); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "alloc resource, rx_buffer_len = %d\n", + pdata->rx_buffer_len); + + ret = phytmac_alloc_tx_resource(pdata); + if (ret) + return ret; + + ret = phytmac_alloc_rx_resource(pdata); + if (ret) { + phytmac_free_tx_resource(pdata); + return ret; + } + + return 0; +} + +static void phytmac_free_resource(struct phytmac *pdata) +{ + phytmac_free_tx_resource(pdata); + phytmac_free_rx_resource(pdata); +} + +static irqreturn_t phytmac_irq(int irq, void *data) +{ + struct phytmac_queue *queue = data; + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 status; + + status = hw_if->get_irq(pdata, queue->index); + + if (netif_msg_intr(pdata)) + netdev_info(pdata->ndev, "phymac irq status = %x\n", status); + + if (unlikely(!status)) + return IRQ_NONE; + + while (status) { + if (status & pdata->rx_irq_mask) { + /* Disable RX interrupts */ + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_COMPLETE); + + if (napi_schedule_prep(&queue->rx_napi)) + __napi_schedule(&queue->rx_napi); + } + + if (status & (PHYTMAC_INT_TX_COMPLETE)) { + /* Disable TX interrupts */ + hw_if->disable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + + if (napi_schedule_prep(&queue->tx_napi)) + __napi_schedule(&queue->tx_napi); + } + + if (status & PHYTMAC_INT_TX_ERR) + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_ERR); + + if (status & PHYTMAC_INT_RX_OVERRUN) { + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_OVERRUN); + pdata->stats.rx_overruns++; + } + status = hw_if->get_irq(pdata, queue->index); + } + + return IRQ_HANDLED; +} + +static irqreturn_t phytmac_intx_irq(int irq, void *data) +{ + struct phytmac *pdata = data; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 irq_mask; + int i; + + irq_mask = hw_if->get_intx_mask(pdata); + + if (unlikely(!irq_mask)) + return IRQ_NONE; + + for (i = 0; i < pdata->queues_num; i++) { + if (irq_mask & BIT(i)) + phytmac_irq(irq, &pdata->queues[i]); + } + + return IRQ_HANDLED; +} + +static void phytmac_dump_pkt(struct phytmac *pdata, struct sk_buff *skb, bool tx) +{ + struct net_device *ndev = pdata->ndev; + + if (tx) { + netdev_dbg(ndev, "start_xmit: queue %u len %u head %p data %p tail %p end %p\n", + skb->queue_mapping, skb->len, skb->head, skb->data, + skb_tail_pointer(skb), skb_end_pointer(skb)); + } else { + netdev_dbg(ndev, "queue %u received skb of length %u, csum: %08x\n", + skb->queue_mapping, skb->len, skb->csum); + print_hex_dump(KERN_DEBUG, " mac: ", DUMP_PREFIX_ADDRESS, 16, 1, + skb_mac_header(skb), 16, true); + } + + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb->len, true); +} + +static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sk_buff *skb; + unsigned int len; + dma_addr_t addr; + + skb = phytmac_get_rx_skb(queue, queue->rx_tail); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, + "inconsistent Rx descriptor chain\n"); + pdata->ndev->stats.rx_dropped++; + queue->stats.rx_dropped++; + return NULL; + } + + queue->rx_skb[queue->rx_tail & (pdata->rx_ring_size - 1)] = NULL; + len = hw_if->get_rx_pkt_len(pdata, desc); + addr = hw_if->get_desc_addr(desc); + + skb_put(skb, len); + dma_unmap_single(pdata->dev, addr, + pdata->rx_buffer_len, DMA_FROM_DEVICE); + skb->protocol = eth_type_trans(skb, pdata->ndev); + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(desc)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, false); + + return skb; +} + +static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, + unsigned int first_frag, unsigned int last_frag, int len) +{ + unsigned int offset = 0; + unsigned int frag = 0; + unsigned int entry = 0; + dma_addr_t addr = 0; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int frag_len = pdata->rx_buffer_len; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "rx frame %u - %u (len %u)\n", + first_frag, last_frag, len); + + skb = netdev_alloc_skb(pdata->ndev, len); + if (!skb) { + pdata->ndev->stats.rx_dropped++; + netdev_err(pdata->ndev, "rx frame alloc skb failed\n"); + return NULL; + } + + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_put(skb, len); + + for (frag = first_frag; ; frag++) { + if (offset + frag_len > len) { + if (unlikely(frag != last_frag)) { + dev_kfree_skb_any(skb); + return NULL; + } + frag_len = len - offset; + } + + desc = phytmac_get_rx_desc(queue, frag); + addr = hw_if->get_desc_addr(desc); + dma_sync_single_for_cpu(pdata->dev, addr, frag_len, + DMA_FROM_DEVICE); + + entry = frag & (pdata->rx_ring_size - 1); + skb_copy_to_linear_data_offset(skb, offset, queue->rx_skb[entry]->data, frag_len); + + offset += pdata->rx_buffer_len; + + dma_sync_single_for_device(pdata->dev, addr, frag_len, + DMA_FROM_DEVICE); + + if (frag == last_frag) + break; + } + + skb->protocol = eth_type_trans(skb, pdata->ndev); + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, false); + + return skb; +} + +static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + struct sk_buff *skb = NULL; + unsigned int rx_tail = 0; + int first_frag = -1; + int len; + + for (rx_tail = queue->rx_tail; ; rx_tail++) { + desc = phytmac_get_rx_desc(queue, rx_tail); + if (hw_if->rx_pkt_start(desc)) { + if (first_frag != -1) + hw_if->clear_rx_desc(queue, first_frag, rx_tail); + first_frag = rx_tail; + continue; + } + + if (hw_if->rx_pkt_end(desc)) { + queue->rx_tail = rx_tail; + len = hw_if->get_rx_pkt_len(pdata, desc); + skb = phytmac_rx_frame(queue, first_frag, rx_tail, len); + first_frag = -1; + break; + } + } + return skb; +} + +static void phytmac_rx_clean(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int index, space; + dma_addr_t paddr; + struct sk_buff *skb; + unsigned int rx_unclean = 0; + + space = CIRC_SPACE(queue->rx_head, queue->rx_tail, + pdata->rx_ring_size); + + if (space < DEFAULT_RX_DESC_MIN_FREE) + return; + + index = queue->rx_head & (pdata->rx_ring_size - 1); + while (space > 0) { + if (!queue->rx_skb[index]) { + skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, "rx clean alloc skb failed\n"); + break; + } + + paddr = dma_map_single(pdata->dev, skb->data, + pdata->rx_buffer_len, DMA_FROM_DEVICE); + if (dma_mapping_error(pdata->dev, paddr)) { + dev_kfree_skb(skb); + break; + } + + queue->rx_skb[index] = skb; + + hw_if->rx_map(queue, index, paddr); + } + + index = (index + 1) & (pdata->rx_ring_size - 1); + rx_unclean++; + space--; + } + + /* make newly descriptor to hardware */ + wmb(); + hw_if->rx_clean(queue, rx_unclean); + /* make newly descriptor to hardware */ + wmb(); + queue->rx_head += rx_unclean; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); +} + +static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, + int budget) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + int count = 0; + + while (count < budget) { + desc = phytmac_get_rx_desc(queue, queue->rx_tail); + /* make newly desc to cpu */ + rmb(); + + if (!hw_if->rx_complete(desc)) + break; + + /* Ensure ctrl is at least as up-to-date as rxused */ + dma_rmb(); + + if (hw_if->rx_single_buffer(desc)) + skb = phytmac_rx_single(queue, desc); + else + skb = phytmac_rx_mbuffer(queue); + + if (!skb) { + netdev_warn(pdata->ndev, "phytmac rx skb is NULL\n"); + break; + } + + pdata->ndev->stats.rx_packets++; + queue->stats.rx_packets++; + pdata->ndev->stats.rx_bytes += skb->len; + queue->stats.rx_bytes += skb->len; + queue->rx_tail = (queue->rx_tail + 1) & (pdata->rx_ring_size - 1); + + count++; + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + phytmac_ptp_rxstamp(pdata, skb, desc); + + napi_gro_receive(napi, skb); + } + + phytmac_rx_clean(queue); + + return count; +} + +static void phytmac_tx_unmap(struct phytmac *pdata, struct phytmac_tx_skb *tx_skb, int budget) +{ + if (tx_skb->addr) { + if (tx_skb->mapped_as_page) + dma_unmap_page(pdata->dev, tx_skb->addr, + tx_skb->length, DMA_TO_DEVICE); + else + dma_unmap_single(pdata->dev, tx_skb->addr, + tx_skb->length, DMA_TO_DEVICE); + tx_skb->addr = 0; + } + + if (tx_skb->skb) { + napi_consume_skb(tx_skb->skb, budget); + tx_skb->skb = NULL; + } +} + +static int phytmac_maybe_stop_tx_queue(struct phytmac_queue *queue, + unsigned int count) +{ + struct phytmac *pdata = queue->pdata; + int space = CIRC_SPACE(queue->tx_tail, queue->tx_head, + pdata->tx_ring_size); + + if (space < count) { + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "Tx queue %d stopped, not enough descriptors available\n", + queue->index); + + netif_stop_subqueue(pdata->ndev, queue->index); + + return NETDEV_TX_BUSY; + } + + return 0; +} + +static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + int space = CIRC_CNT(queue->tx_tail, queue->tx_head, + pdata->tx_ring_size); + + return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; +} + +static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) +{ + struct phytmac *pdata = queue->pdata; + u16 queue_index = queue->index; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_tx_skb *tx_skb; + struct phytmac_dma_desc *desc; + int complete = 0; + int packet_count = 0; + unsigned int tail = queue->tx_tail; + unsigned int head; + + spin_lock(&pdata->lock); + + for (head = queue->tx_head; head != tail && packet_count < budget; ) { + struct sk_buff *skb; + + desc = phytmac_get_tx_desc(queue, head); + /* make newly desc to cpu */ + rmb(); + if (!hw_if->tx_complete(desc)) + break; + + /* Process all buffers of the current transmitted frame */ + for (;; head++) { + tx_skb = phytmac_get_tx_skb(queue, head); + skb = tx_skb->skb; + + if (skb) { + complete = 1; + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + if (unlikely(skb_shinfo(skb)->tx_flags & + SKBTX_HW_TSTAMP) && + !phytmac_ptp_one_step(skb)) { + phytmac_ptp_txstamp(queue, skb, desc); + } + } + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", + head, tx_skb->skb->data); + + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->skb->len; + queue->stats.tx_bytes += tx_skb->skb->len; + packet_count++; + } + + /* Now we can safely release resources */ + phytmac_tx_unmap(pdata, tx_skb, budget); + + if (complete) { + complete = 0; + break; + } + } + + head++; + if (head >= pdata->tx_ring_size) + head &= (pdata->tx_ring_size - 1); + } + + queue->tx_head = head; + if (__netif_subqueue_stopped(pdata->ndev, queue_index) && + (phytmac_maybe_wake_tx_queue(queue))) + netif_wake_subqueue(pdata->ndev, queue_index); + spin_unlock(&pdata->lock); + + return packet_count; +} + +static int phytmac_rx_poll(struct napi_struct *napi, int budget) +{ + struct phytmac_queue *queue = container_of(napi, struct phytmac_queue, rx_napi); + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + int work_done; + + work_done = phytmac_rx(queue, napi, budget); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "RX poll: queue = %u, work_done = %d, budget = %d\n", + (unsigned int)(queue->index), work_done, budget); + if (work_done < budget && napi_complete_done(napi, work_done)) { + hw_if->enable_irq(pdata, queue->index, pdata->rx_irq_mask); + + desc = phytmac_get_rx_desc(queue, queue->rx_tail); + /* make newly desc to cpu */ + rmb(); + + if (hw_if->rx_complete(desc)) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_COMPLETE); + + napi_schedule(napi); + } + } + + return work_done; +} + +static int phytmac_tx_poll(struct napi_struct *napi, int budget) +{ + struct phytmac_queue *queue = container_of(napi, struct phytmac_queue, tx_napi); + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + int work_done; + + work_done = phytmac_tx_clean(queue, budget); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "TX poll: queue = %u, work_done = %d, budget = %d\n", + (unsigned int)(queue->index), work_done, budget); + if (work_done < budget && napi_complete_done(napi, work_done)) { + hw_if->enable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + if (queue->tx_head != queue->tx_tail) { + desc = phytmac_get_tx_desc(queue, queue->tx_head); + /* make newly desc to cpu */ + rmb(); + + if (hw_if->tx_complete(desc)) { + hw_if->disable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + + napi_schedule(napi); + } + } + } + + return work_done; +} + +static inline int phytmac_clear_csum(struct sk_buff *skb) +{ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + /* make sure we can modify the header */ + if (unlikely(skb_cow_head(skb, 0))) + return -1; + + *(__sum16 *)(skb_checksum_start(skb) + skb->csum_offset) = 0; + return 0; +} + +static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) +{ + bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) || + skb_is_nonlinear(*skb); + int padlen = ETH_ZLEN - (*skb)->len; + int headroom = skb_headroom(*skb); + int tailroom = skb_tailroom(*skb); + struct sk_buff *nskb; + u32 fcs; + int i; + + if ((ndev->features & NETIF_F_HW_CSUM) || + !((*skb)->ip_summed != CHECKSUM_PARTIAL) || + skb_shinfo(*skb)->gso_size || phytmac_ptp_one_step(*skb)) + return 0; + + if (padlen <= 0) { + if (tailroom >= ETH_FCS_LEN) + goto add_fcs; + else if (!cloned && headroom + tailroom >= ETH_FCS_LEN) + padlen = 0; + else + padlen = ETH_FCS_LEN; + } else { + padlen += ETH_FCS_LEN; + } + + if (!cloned && headroom + tailroom >= padlen) { + (*skb)->data = memmove((*skb)->head, (*skb)->data, (*skb)->len); + skb_set_tail_pointer(*skb, (*skb)->len); + } else { + nskb = skb_copy_expand(*skb, 0, padlen, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; + + dev_consume_skb_any(*skb); + *skb = nskb; + } + + if (padlen > ETH_FCS_LEN) + skb_put_zero(*skb, padlen - ETH_FCS_LEN); + +add_fcs: + fcs = crc32_le(~0, (*skb)->data, (*skb)->len); + fcs = ~fcs; + + for (i = 0; i < 4; ++i) + skb_put_u8(*skb, (fcs >> (i * 8)) & 0xff); + return 0; +} + +static int phytmac_packet_info(struct phytmac *pdata, + struct phytmac_queue *queue, struct sk_buff *skb, + struct packet_info *packet) +{ + int is_lso; + unsigned int hdrlen, f; + int desc_cnt; + + memset(packet, 0, sizeof(struct packet_info)); + + is_lso = (skb_shinfo(skb)->gso_size != 0); + + if (is_lso) { + /* length of headers */ + if (ip_hdr(skb)->protocol == IPPROTO_UDP) { + /* only queue eth + ip headers separately for UDP */ + hdrlen = skb_transport_offset(skb); + packet->lso = LSO_UFO; + packet->mss = skb_shinfo(skb)->gso_size + hdrlen + ETH_FCS_LEN; + } else { + hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + packet->lso = LSO_TSO; + packet->mss = skb_shinfo(skb)->gso_size; + } + + if (skb_headlen(skb) < hdrlen) { + dev_err(pdata->dev, "Error - LSO headers fragmented!!!\n"); + return NETDEV_TX_BUSY; + } + } else { + hdrlen = min(skb_headlen(skb), pdata->max_tx_length); + packet->lso = 0; + packet->mss = 0; + } + + packet->hdrlen = hdrlen; + + if (is_lso && (skb_headlen(skb) > hdrlen)) + desc_cnt = TXD_USE_COUNT(pdata, (skb_headlen(skb) - hdrlen)) + 1; + else + desc_cnt = TXD_USE_COUNT(pdata, hdrlen); + + for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) + desc_cnt += TXD_USE_COUNT(pdata, skb_frag_size(&skb_shinfo(skb)->frags[f])); + packet->desc_cnt = desc_cnt; + + if ((!(pdata->ndev->features & NETIF_F_HW_CSUM)) && + skb->ip_summed != CHECKSUM_PARTIAL && + !is_lso && + !phytmac_ptp_one_step(skb)) + packet->nocrc = 1; + else + packet->nocrc = 0; + + if (netif_msg_pktdata(pdata)) { + netdev_info(pdata->ndev, "packet info: desc_cnt=%d, nocrc=%d,ip_summed=%d\n", + desc_cnt, packet->nocrc, skb->ip_summed); + netdev_info(pdata->ndev, "packet info: mss=%d, lso=%d,skb_len=%d, nr_frags=%d\n", + packet->mss, packet->lso, skb->len, skb_shinfo(skb)->nr_frags); + } + + return 0; +} + +static unsigned int phytmac_tx_map(struct phytmac *pdata, + struct phytmac_queue *queue, + struct sk_buff *skb, + struct packet_info *packet) +{ + dma_addr_t mapping; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int len, i, tx_tail = queue->tx_tail; + struct phytmac_tx_skb *tx_skb = NULL; + unsigned int offset, size, count = 0; + unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; + + len = skb_headlen(skb); + size = packet->hdrlen; + + offset = 0; + tx_tail = queue->tx_tail; + while (len) { + tx_skb = phytmac_get_tx_skb(queue, tx_tail); + + mapping = dma_map_single(pdata->dev, + skb->data + offset, + size, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, mapping)) + goto dma_error; + + /* Save info to properly release resources */ + tx_skb->skb = NULL; + tx_skb->addr = mapping; + tx_skb->length = size; + tx_skb->mapped_as_page = false; + + len -= size; + offset += size; + count++; + tx_tail++; + + size = min(len, pdata->max_tx_length); + } + + /* Then, map paged data from fragments */ + for (f = 0; f < nr_frags; f++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + + len = skb_frag_size(frag); + offset = 0; + while (len) { + size = min(len, pdata->max_tx_length); + tx_skb = phytmac_get_tx_skb(queue, tx_tail); + mapping = skb_frag_dma_map(pdata->dev, frag, + offset, size, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, mapping)) + goto dma_error; + + /* Save info to properly release resources */ + tx_skb->skb = NULL; + tx_skb->addr = mapping; + tx_skb->length = size; + tx_skb->mapped_as_page = true; + + len -= size; + offset += size; + count++; + tx_tail++; + } + } + + /* Should never happen */ + if (unlikely(!tx_skb)) { + netdev_err(pdata->ndev, "BUG! empty skb!\n"); + return 0; + } + + /* This is the last buffer of the frame: save socket buffer */ + tx_skb->skb = skb; + + if (hw_if->tx_map(queue, tx_tail, packet)) { + netdev_err(pdata->ndev, "BUG!hw tx map failed!\n"); + return 0; + } + + queue->tx_tail = tx_tail & (pdata->tx_ring_size - 1); + + return count; + +dma_error: + netdev_err(pdata->ndev, "TX DMA map failed\n"); + + for (i = queue->tx_tail; i != tx_tail; i++) { + tx_skb = phytmac_get_tx_skb(queue, i); + phytmac_tx_unmap(pdata, tx_skb, 0); + } + + return 0; +} + +static inline void phytmac_init_ring(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned int q = 0; + + for (queue = pdata->queues; q < pdata->queues_num; ++q) { + queue->tx_head = 0; + queue->tx_tail = 0; + hw_if->clear_tx_desc(queue); + + queue->rx_head = 0; + queue->rx_tail = 0; + phytmac_rx_clean(queue); + ++queue; + } + + hw_if->init_ring_hw(pdata); +} + +static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + u16 queue_index = skb->queue_mapping; + struct phytmac_queue *queue = &pdata->queues[queue_index]; + netdev_tx_t ret = NETDEV_TX_OK; + struct packet_info packet; + unsigned long flags; + + if (phytmac_clear_csum(skb)) { + dev_kfree_skb_any(skb); + return ret; + } + + if (phytmac_add_fcs(&skb, ndev)) { + dev_kfree_skb_any(skb); + return ret; + } + + ret = phytmac_packet_info(pdata, queue, skb, &packet); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, true); + + spin_lock_irqsave(&pdata->lock, flags); + /* Check that there are enough descriptors available */ + ret = phytmac_maybe_stop_tx_queue(queue, packet.desc_cnt); + if (ret) + goto tx_return; + + /* Map socket buffer for DMA transfer */ + if (!phytmac_tx_map(pdata, queue, skb, &packet)) { + dev_kfree_skb_any(skb); + goto tx_return; + } + + skb_tx_timestamp(skb); + /* Make newly descriptor to hardware */ + wmb(); + + hw_if->transmit(queue); + +tx_return: + spin_unlock_irqrestore(&pdata->lock, flags); + return ret; +} + +static int phytmac_phylink_connect(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct phy_device *phydev; + struct fwnode_handle *fwnode = dev_fwnode(pdata->dev); + int ret = 0; + + if (fwnode) + ret = phylink_fwnode_phy_connect(pdata->phylink, fwnode, 0); + + if (!fwnode || ret) { + if (pdata->mii_bus) { + phydev = phy_find_first(pdata->mii_bus); + if (!phydev) { + dev_err(pdata->dev, "no PHY found\n"); + return -ENXIO; + } + /* attach the mac to the phy */ + ret = phylink_connect_phy(pdata->phylink, phydev); + } else { + netdev_err(ndev, "Not mii register\n"); + return -ENXIO; + } + } + + if (ret) { + netdev_err(ndev, "Could not attach PHY (%d)\n", ret); + return ret; + } + + return 0; +} + +int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + return 0; +} + +void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex) +{ + struct phytmac *pdata = container_of(pcs, struct phytmac, phylink_pcs); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "pcs link up, interface = %s, speed = %d, duplex = %d\n", + phy_modes(interface), speed, duplex); + hw_if->pcs_linkup(pdata, interface, speed, duplex); +} + +static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { + .pcs_config = phytmac_pcs_config, + .pcs_link_up = phytmac_pcs_link_up, +}; + +static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER || + interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || + interface == PHY_INTERFACE_MODE_2500BASEX) { + pdata->phylink_pcs.ops = &phytmac_pcs_phylink_ops; + } else { + pdata->phylink_pcs.ops = NULL; + } + + return &pdata->phylink_pcs; +} + +static void phytmac_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + if (netif_msg_link(pdata)) { + netdev_info(pdata->ndev, "mac config interface=%s, mode=%d\n", + phy_modes(state->interface), mode); + } + + spin_lock_irqsave(&pdata->lock, flags); + hw_if->mac_config(pdata, mode, state); + spin_unlock_irqrestore(&pdata->lock, flags); +} + +static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned int q; + unsigned long flags; + struct phytmac_tx_skb *tx_skb; + int i; + + if (netif_msg_link(pdata)) { + netdev_info(ndev, "link down interface:%s, mode=%d\n", + phy_modes(interface), mode); + } + + if (pdata->use_ncsi) + ncsi_stop_dev(pdata->ncsidev); + + spin_lock_irqsave(&pdata->lock, flags); + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + hw_if->clear_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + } + + /* Disable Rx and Tx */ + hw_if->enable_network(pdata, false, PHYTMAC_RX | PHYTMAC_TX); + + /* Tx clean */ + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + for (i = 0; i < pdata->tx_ring_size; i++) { + tx_skb = phytmac_get_tx_skb(queue, i); + if (tx_skb) + phytmac_tx_unmap(pdata, tx_skb, 0); + } + } + + spin_unlock_irqrestore(&pdata->lock, flags); + + netif_tx_stop_all_queues(ndev); +} + +static void phytmac_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned long flags; + unsigned int q; + int ret; + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "link up interface:%s, speed:%d, duplex:%s\n", + phy_modes(interface), speed, duplex ? "full-duplex" : "half-duplex"); + + spin_lock_irqsave(&pdata->lock, flags); + + hw_if->mac_linkup(pdata, interface, speed, duplex); + + if (rx_pause != pdata->pause) { + hw_if->enable_pause(pdata, rx_pause); + pdata->pause = rx_pause; + } + + phytmac_init_ring(pdata); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) + hw_if->enable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + + /* Enable Rx and Tx */ + hw_if->enable_network(pdata, true, PHYTMAC_RX | PHYTMAC_TX); + spin_unlock_irqrestore(&pdata->lock, flags); + + if (pdata->use_ncsi) { + /* Start the NCSI device */ + ret = ncsi_start_dev(pdata->ncsidev); + if (ret) { + netdev_err(pdata->ndev, "Ncsi start dev failed (error %d)\n", ret); + return; + } + } + + netif_tx_wake_all_queues(ndev); +} + +int phytmac_mdio_register(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + pdata->mii_bus = mdiobus_alloc(); + if (!pdata->mii_bus) { + ret = -ENOMEM; + goto free_mdio; + } + + pdata->mii_bus->name = "phytmac_mii_bus"; + pdata->mii_bus->read = &phytmac_mdio_read_c22; + pdata->mii_bus->write = &phytmac_mdio_write_c22; + pdata->mii_bus->read_c45 = &phytmac_mdio_read_c45; + pdata->mii_bus->write_c45 = &phytmac_mdio_write_c45; + + if (pdata->platdev) { + snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", + pdata->mii_bus->name, netdev_name(pdata->ndev)); + } else if (pdata->pcidev) { + snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", + pdata->mii_bus->name, pci_name(pdata->pcidev)); + } else { + ret = -ENOMEM; + goto free_mdio; + } + + pdata->mii_bus->priv = pdata; + pdata->mii_bus->parent = pdata->dev; + + hw_if->enable_mdio_control(pdata, 1); + + return mdiobus_register(pdata->mii_bus); +free_mdio: + mdiobus_free(pdata->mii_bus); + return ret; +} + +static void phytmac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + struct phytmac *pdata = container_of(config, struct phytmac, phylink_config); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + state->link = hw_if->get_link(pdata, state->interface); +} + +static void phytmac_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct net_device *ndev = to_net_dev(config->dev); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + struct phytmac *pdata = netdev_priv(ndev); + + if (state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_2500BASEX && + state->interface != PHY_INTERFACE_MODE_5GBASER && + state->interface != PHY_INTERFACE_MODE_10GBASER && + state->interface != PHY_INTERFACE_MODE_USXGMII && + !phy_interface_mode_is_rgmii(state->interface)) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + phylink_set(mask, Asym_Pause); + + if (state->interface == PHY_INTERFACE_MODE_10GBASER || + state->interface == PHY_INTERFACE_MODE_USXGMII) { + pdata->speed = state->speed; + pdata->duplex = state->duplex; + if (pdata->speed == SPEED_5000) { + phylink_set(mask, 5000baseT_Full); + } else { + phylink_set(mask, 10000baseCR_Full); + phylink_set(mask, 10000baseER_Full); + phylink_set(mask, 10000baseKR_Full); + phylink_set(mask, 10000baseLR_Full); + phylink_set(mask, 10000baseLRM_Full); + phylink_set(mask, 10000baseSR_Full); + phylink_set(mask, 10000baseT_Full); + } + } + + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + phylink_set(mask, 2500baseX_Full); + + if (state->interface == PHY_INTERFACE_MODE_5GBASER) + phylink_set(mask, 5000baseT_Full); + + if (state->interface == PHY_INTERFACE_MODE_1000BASEX || + state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_rgmii(state->interface)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 1000baseT_Half); + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + } + + bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static const struct phylink_mac_ops phytmac_phylink_ops = { + .validate = phytmac_validate, + .mac_select_pcs = phytmac_mac_select_pcs, + .mac_config = phytmac_mac_config, + .mac_link_down = phytmac_mac_link_down, + .mac_link_up = phytmac_mac_link_up, +}; + +static inline void set_phy_interface(unsigned long *intf) +{ + __set_bit(PHY_INTERFACE_MODE_SGMII, intf); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, intf); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, intf); + __set_bit(PHY_INTERFACE_MODE_USXGMII, intf); + __set_bit(PHY_INTERFACE_MODE_10GBASER, intf); +} + +static int phytmac_phylink_create(struct phytmac *pdata) +{ + struct fwnode_handle *fw_node = dev_fwnode(pdata->dev); + + pdata->phylink_config.dev = &pdata->ndev->dev; + pdata->phylink_config.type = PHYLINK_NETDEV; + if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX || + pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX || + pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + pdata->phylink_config.poll_fixed_state = true; + pdata->phylink_config.get_fixed_state = phytmac_pcs_get_state; + pdata->phylink_pcs.ops = &phytmac_pcs_phylink_ops; + } + + set_phy_interface(pdata->phylink_config.supported_interfaces); + pdata->phylink = phylink_create(&pdata->phylink_config, fw_node, + pdata->phy_interface, &phytmac_phylink_ops); + if (IS_ERR(pdata->phylink)) { + dev_err(pdata->dev, "Could not create a phylink instance (%ld)\n", + PTR_ERR(pdata->phylink)); + return PTR_ERR(pdata->phylink); + } + + return 0; +} + +static int phytmac_open(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int q = 0; + int ret; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "open\n"); + + /* phytmac_powerup */ + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + ret = hw_if->get_feature(pdata); + if (ret) { + netdev_err(ndev, "phytmac get features failed\n"); + return ret; + } + + hw_if->reset_hw(pdata); + + ret = phytmac_get_mac_address(pdata); + if (ret) { + netdev_err(ndev, "phytmac get mac address failed\n"); + goto reset_hw; + } + + ret = netif_set_real_num_tx_queues(ndev, pdata->queues_num); + if (ret) { + netdev_err(ndev, "error setting real tx queue number\n"); + return ret; + } + ret = netif_set_real_num_rx_queues(ndev, pdata->queues_num); + if (ret) { + netdev_err(ndev, "error setting real tx queue number\n"); + return ret; + } + + /* RX buffers initialization */ + ret = phytmac_alloc_resource(pdata); + if (ret) { + netdev_err(ndev, "Unable to allocate DMA memory (error %d)\n", + ret); + goto reset_hw; + } + + for (queue = pdata->queues; q < pdata->queues_num; ++q) { + napi_enable(&queue->tx_napi); + napi_enable(&queue->rx_napi); + ++queue; + } + + phytmac_init_ring(pdata); + hw_if->init_hw(pdata); + + ret = phytmac_phylink_connect(pdata); + if (ret) { + netdev_err(ndev, "phylink connet failed,(error %d)\n", + ret); + goto reset_hw; + } + + phylink_start(pdata->phylink); + + netif_tx_start_all_queues(pdata->ndev); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + ret = phytmac_ptp_register(pdata); + if (ret) { + netdev_err(ndev, "ptp register failed, (error %d)\n", + ret); + goto reset_hw; + } + + phytmac_ptp_init(pdata->ndev); + } + + return 0; + +reset_hw: + hw_if->reset_hw(pdata); + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + ++queue; + } + phytmac_free_resource(pdata); + return ret; +} + +static int phytmac_close(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + unsigned int q; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "close"); + + netif_tx_stop_all_queues(ndev); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + } + + phylink_stop(pdata->phylink); + phylink_disconnect_phy(pdata->phylink); + + netif_carrier_off(ndev); + + spin_lock_irqsave(&pdata->lock, flags); + hw_if->reset_hw(pdata); + spin_unlock_irqrestore(&pdata->lock, flags); + + phytmac_free_resource(pdata); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + phytmac_ptp_unregister(pdata); + + /* phytmac_powerup */ + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); + + return 0; +} + +static int phytmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct phytmac *pdata = netdev_priv(dev); + int ret; + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + ret = phylink_mii_ioctl(pdata->phylink, rq, cmd); + break; +#ifdef CONFIG_PHYTMAC_ENABLE_PTP + case SIOCSHWTSTAMP: + ret = phytmac_ptp_set_ts_config(dev, rq, cmd); + break; + case SIOCGHWTSTAMP: + ret = phytmac_ptp_get_ts_config(dev, rq); + break; +#endif + default: + break; + } + + return ret; +} + +static inline int phytmac_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct phytmac *pdata = netdev_priv(netdev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + netdev_features_t changed = features ^ netdev->features; + + /* TX checksum offload */ + if (changed & NETIF_F_HW_CSUM) { + if (features & NETIF_F_HW_CSUM) + hw_if->enable_tx_csum(pdata, 1); + else + hw_if->enable_tx_csum(pdata, 0); + } + + /* RX checksum offload */ + if (changed & NETIF_F_RXCSUM) { + if (features & NETIF_F_RXCSUM && + !(netdev->flags & IFF_PROMISC)) + hw_if->enable_rx_csum(pdata, 1); + else + hw_if->enable_rx_csum(pdata, 0); + } + return 0; +} + +static netdev_features_t phytmac_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + unsigned int nr_frags, f; + unsigned int hdrlen; + + /* there is only one buffer or protocol is not UDP */ + if (!skb_is_nonlinear(skb) || (ip_hdr(skb)->protocol != IPPROTO_UDP)) + return features; + + /* length of header */ + hdrlen = skb_transport_offset(skb); + + if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, PHYTMAC_TX_LEN_ALIGN)) + return features & ~NETIF_F_TSO; + + nr_frags = skb_shinfo(skb)->nr_frags; + /* No need to check last fragment */ + nr_frags--; + for (f = 0; f < nr_frags; f++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + + if (!IS_ALIGNED(skb_frag_size(frag), PHYTMAC_TX_LEN_ALIGN)) + return features & ~NETIF_F_TSO; + } + return features; +} + +int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) +{ + int ret = 0; + int reset = 0; + + if (netif_running(pdata->ndev)) { + reset = 1; + phytmac_close(pdata->ndev); + } + + pdata->rx_ring_size = rx_size; + pdata->tx_ring_size = tx_size; + + if (reset) + phytmac_open(pdata->ndev); + + return ret; +} + +static const struct net_device_ops phytmac_netdev_ops = { + .ndo_open = phytmac_open, + .ndo_stop = phytmac_close, + .ndo_start_xmit = phytmac_start_xmit, + .ndo_set_rx_mode = phytmac_set_rx_mode, + .ndo_get_stats = phytmac_get_stats, + .ndo_eth_ioctl = phytmac_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = phytmac_change_mtu, + .ndo_set_mac_address = phytmac_set_mac_address, + .ndo_set_features = phytmac_set_features, + .ndo_features_check = phytmac_features_check, + .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, +}; + +static int phytmac_init(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + unsigned int q; + struct phytmac_queue *queue; + int ret; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phytmac init !\n"); + + spin_lock_init(&pdata->lock); + + /* set the queue register mapping once for all: queue0 has a special + * register mapping but we don't want to test the queue index then + * compute the corresponding register offset at run time. + */ + for (q = 0; q < pdata->queues_num; ++q) { + queue = &pdata->queues[q]; + queue->pdata = pdata; + queue->index = q; + spin_lock_init(&queue->tx_lock); + + netif_napi_add(ndev, &queue->tx_napi, phytmac_tx_poll); + netif_napi_add(ndev, &queue->rx_napi, phytmac_rx_poll); + + if (pdata->irq_type == IRQ_TYPE_INT || pdata->irq_type == IRQ_TYPE_MSI) { + queue->irq = pdata->queue_irq[q]; + if (pdata->irq_type == IRQ_TYPE_INT) + ret = devm_request_irq(pdata->dev, queue->irq, phytmac_irq, + IRQF_SHARED, ndev->name, queue); + else + ret = devm_request_irq(pdata->dev, queue->irq, phytmac_irq, + 0, ndev->name, queue); + + if (ret) { + dev_err(pdata->dev, + "Unable to request IRQ %d (error %d)\n", + queue->irq, ret); + return ret; + } + } + } + + if (pdata->irq_type == IRQ_TYPE_INTX) { + ret = devm_request_irq(pdata->dev, pdata->queue_irq[0], phytmac_intx_irq, + IRQF_SHARED, ndev->name, pdata); + if (ret) { + dev_err(pdata->dev, + "Unable to request INTX IRQ %d (error %d)\n", + pdata->queue_irq[0], ret); + return ret; + } + } + + ndev->netdev_ops = &phytmac_netdev_ops; + phytmac_set_ethtool_ops(ndev); + eth_hw_addr_random(pdata->ndev); + + if (ndev->hw_features & NETIF_F_NTUPLE) { + INIT_LIST_HEAD(&pdata->rx_fs_list.list); + pdata->rx_fs_list.count = 0; + spin_lock_init(&pdata->rx_fs_lock); + } + + device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + + return 0; +} + +void phytmac_default_config(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + + pdata->rx_irq_mask = PHYTMAC_RX_INT_FLAGS; + pdata->tx_irq_mask = PHYTMAC_TX_INT_FLAGS; + pdata->tx_ring_size = DEFAULT_TX_RING_SIZE; + pdata->rx_ring_size = DEFAULT_RX_RING_SIZE; + pdata->max_tx_length = PHYTMAC_MAX_TX_LEN; + pdata->min_tx_length = PHYTMAC_MIN_TX_LEN; + pdata->pause = true; + + ndev->hw_features = NETIF_F_SG; + + if (pdata->capacities & PHYTMAC_CAPS_LSO) + ndev->hw_features |= NETIF_F_TSO; + + if (pdata->use_ncsi) { + ndev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + } else { + ndev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + } + + if (pdata->capacities & PHYTMAC_CAPS_SG_DISABLED) + ndev->hw_features &= ~NETIF_F_SG; + + ndev->hw_features |= NETIF_F_NTUPLE; + + ndev->min_mtu = ETH_MIN_MTU; + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + ndev->max_mtu = pdata->jumbo_len - ETH_HLEN - ETH_FCS_LEN; + else + ndev->max_mtu = ETH_DATA_LEN; + + ndev->features = ndev->hw_features; +} + +static void phytmac_ncsi_handler(struct ncsi_dev *nd) +{ + if (unlikely(nd->state != ncsi_dev_state_functional)) + return; + + netdev_dbg(nd->dev, "NCSI interface %s\n", + nd->link_up ? "up" : "down"); +} + +int phytmac_drv_probe(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct device *dev = pdata->dev; + int ret = 0; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phytmac drv probe start\n"); + + phytmac_default_config(pdata); + + if (dma_set_mask(dev, DMA_BIT_MASK(40)) || + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40))) { + dev_err(dev, "dma_set_mask or coherent failed\n"); + return 1; + } + + ret = phytmac_init(pdata); + if (ret) + goto err_out_free_netdev; + + if (pdata->use_ncsi) { + pdata->ncsidev = ncsi_register_dev(ndev, phytmac_ncsi_handler); + if (!pdata->ncsidev) + goto err_out_free_netdev; + } + + netif_carrier_off(ndev); + ret = register_netdev(ndev); + if (ret) { + dev_err(pdata->dev, "Cannot register net device, aborting.\n"); + goto err_out_free_netdev; + } + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "probe success!Phytium %s at 0x%08lx irq %d (%pM)\n", + "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); + + if (pdata->use_mii && !pdata->mii_bus) { + ret = phytmac_mdio_register(pdata); + if (ret) { + netdev_err(ndev, "MDIO bus registration failed\n"); + goto err_phylink_init; + } + } + + ret = phytmac_phylink_create(pdata); + if (ret) { + netdev_err(ndev, "phytmac phylink create failed, error %d\n", ret); + goto err_phylink_init; + } + + return 0; + +err_phylink_init: + if (pdata->mii_bus) + mdiobus_unregister(pdata->mii_bus); + +err_out_free_netdev: + free_netdev(ndev); + + return ret; +} +EXPORT_SYMBOL_GPL(phytmac_drv_probe); + +int phytmac_drv_remove(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + + if (ndev) { + if (pdata->use_ncsi && pdata->ncsidev) + ncsi_unregister_dev(pdata->ncsidev); + + unregister_netdev(ndev); + + if (pdata->use_mii && pdata->mii_bus) { + mdiobus_unregister(pdata->mii_bus); + mdiobus_free(pdata->mii_bus); + } + + if (pdata->phylink) + phylink_destroy(pdata->phylink); + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_remove); + +int phytmac_drv_suspend(struct phytmac *pdata) +{ + int q; + unsigned long flags; + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (!netif_running(pdata->ndev)) + return 0; + + if (pdata->power_state == PHYTMAC_POWEROFF) + return 0; + + netif_carrier_off(pdata->ndev); + netif_device_detach(pdata->ndev); + + /* napi_disable */ + for (q = 0, queue = pdata->queues; q < pdata->queues_num; + ++q, ++queue) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + } + + if (pdata->wol) { + hw_if->set_wol(pdata, pdata->wol); + } else { + rtnl_lock(); + phylink_stop(pdata->phylink); + rtnl_unlock(); + spin_lock_irqsave(&pdata->lock, flags); + hw_if->reset_hw(pdata); + hw_if->poweron(pdata, PHYTMAC_POWEROFF); + spin_unlock_irqrestore(&pdata->lock, flags); + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_suspend); + +int phytmac_drv_resume(struct phytmac *pdata) +{ + int q; + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct ethtool_rx_fs_item *item; + + if (!netif_running(pdata->ndev)) + return 0; + + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + if (pdata->wol) { + hw_if->set_wol(pdata, 0); + rtnl_lock(); + phylink_stop(pdata->phylink); + rtnl_unlock(); + } + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; + ++q, ++queue) { + napi_enable(&queue->tx_napi); + napi_enable(&queue->rx_napi); + } + + hw_if->init_hw(pdata); + phytmac_set_rx_mode(pdata->ndev); + phytmac_set_features(pdata->ndev, pdata->ndev->features); + list_for_each_entry(item, &pdata->rx_fs_list.list, list) + hw_if->add_fdir_entry(pdata, &item->fs); + + rtnl_lock(); + phylink_start(pdata->phylink); + rtnl_unlock(); + + netif_device_attach(pdata->ndev); + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_resume); + +struct phytmac *phytmac_alloc_pdata(struct device *dev) +{ + struct phytmac *pdata; + struct net_device *netdev; + + netdev = alloc_etherdev_mq(sizeof(struct phytmac), + PHYTMAC_MAX_QUEUES); + if (!netdev) { + dev_err(dev, "alloc_etherdev_mq failed\n"); + return ERR_PTR(-ENOMEM); + } + SET_NETDEV_DEV(netdev, dev); + pdata = netdev_priv(netdev); + pdata->ndev = netdev; + pdata->dev = dev; + + spin_lock_init(&pdata->lock); + spin_lock_init(&pdata->msg_lock); + spin_lock_init(&pdata->ts_clk_lock); + pdata->msg_enable = netif_msg_init(debug, PHYTMAC_DEFAULT_MSG_ENABLE); + + return pdata; +} +EXPORT_SYMBOL_GPL(phytmac_alloc_pdata); + +void phytmac_free_pdata(struct phytmac *pdata) +{ + struct net_device *netdev = pdata->ndev; + + free_netdev(netdev); +} +EXPORT_SYMBOL_GPL(phytmac_free_pdata); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium Ethernet driver"); +MODULE_AUTHOR("Wenting Song"); +MODULE_ALIAS("platform:phytmac"); + diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c new file mode 100644 index 00000000000000..fd21bf80f13883 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium GMAC PCI wrapper. + * + */ + +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" + +#define PCI_DEVICE_ID_GMAC 0xDC3B +#define PCI_SUBDEVICE_ID_SGMII 0x1000 +#define PCI_SUBDEVICE_ID_1000BASEX 0x1001 +#define PCI_SUBDEVICE_ID_2500BASEX 0x1002 +#define PCI_SUBDEVICE_ID_5GBASER 0x1003 +#define PCI_SUBDEVICE_ID_USXGMII 0x1004 +#define PCI_SUBDEVICE_ID_10GBASER 0x1005 + +struct phytmac_data { + struct phytmac_hw_if *hw_if; + u32 caps; + u32 tsu_rate; + u16 queue_num; + int speed; + bool duplex; + bool use_mii; + bool use_ncsi; + phy_interface_t interface; + const struct property_entry *properties; +}; + +static const u32 fixedlink[][5] = { + {0, 1, 1000, 1, 0}, + {0, 1, 2500, 1, 0}, + {0, 1, 5000, 1, 0}, + {0, 1, 10000, 1, 0}, +}; + +static const struct property_entry fl_properties[][2] = { + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[0]), {} }, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[1]), {} }, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[2]), {} }, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[3]), {} }, +}; + +static int phytmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct phytmac_data *data = (struct phytmac_data *)id->driver_data; + struct phytmac *pdata; + struct device *dev = &pdev->dev; + void __iomem * const *iomap_table; + struct fwnode_handle *fw_node = NULL; + int bar_mask; + int ret, i; + + pdata = phytmac_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->pcidev = pdev; + pci_set_drvdata(pdev, pdata); + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(dev, "pcim_enable_device failed\n"); + goto err_pci_enable; + } + + /* Obtain the mmio areas for the device */ + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_DRV_NAME); + if (ret) { + dev_err(dev, "pcim_iomap_regions failed\n"); + goto err_pci_enable; + } + + iomap_table = pcim_iomap_table(pdev); + if (!iomap_table) { + dev_err(dev, "pcim_iomap_table failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->mac_regs = iomap_table[0]; + if (!pdata->mac_regs) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->msg_regs = iomap_table[1]; + if (!pdata->msg_regs) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pci_set_master(pdev); + + /* para */ + pdata->dma_burst_length = DEFAULT_DMA_BURST_LENGTH; + pdata->jumbo_len = DEFAULT_DMA_BURST_LENGTH; + pdata->wol |= PHYTMAC_WAKE_MAGIC; + pdata->use_ncsi = data->use_ncsi; + pdata->use_mii = data->use_mii; + pdata->phy_interface = data->interface; + pdata->queues_num = data->queue_num; + pdata->capacities = data->caps; + pdata->hw_if = data->hw_if; + + if (!pdata->use_mii) { + fw_node = fwnode_create_software_node(data->properties, NULL); + if (IS_ERR(fw_node)) { + dev_err(dev, "Failed to create software node\n"); + goto err_pci_enable; + } + pdata->dev->fwnode = fw_node; + } + + /* irq */ + ret = pci_alloc_irq_vectors(pdev, pdata->queues_num, pdata->queues_num, PCI_IRQ_MSI); + if (ret < 0) { + pdata->irq_type = IRQ_TYPE_INTX; + pdata->queue_irq[0] = pdev->irq; + } else { + pdata->irq_type = IRQ_TYPE_MSI; + for (i = 0; i < pdata->queues_num; i++) + pdata->queue_irq[i] = pci_irq_vector(pdev, i); + } + + /* Configure the netdev resource */ + ret = phytmac_drv_probe(pdata); + if (ret) + goto err_irq_vectors; + + netdev_notice(pdata->ndev, "net device enabled\n"); + + return 0; + +err_irq_vectors: + if (fw_node) + fwnode_remove_software_node(fw_node); + pci_free_irq_vectors(pdata->pcidev); + +err_pci_enable: + phytmac_free_pdata(pdata); + +err_alloc: + dev_notice(dev, "net device not enabled\n"); + + return ret; +} + +static void phytmac_pci_remove(struct pci_dev *pdev) +{ + struct phytmac *pdata = pci_get_drvdata(pdev); + struct fwnode_handle *fw_node = dev_fwnode(pdata->dev); + int i = 0; + int bar_mask; + + if (fw_node) { + fwnode_remove_software_node(fw_node); + pdata->dev->fwnode = NULL; + } + + phytmac_drv_remove(pdata); + + for (i = 0; i < pdata->queues_num; i++) + free_irq(pci_irq_vector(pdev, i), &pdata->queues[i]); + pci_free_irq_vectors(pdev); + + phytmac_free_pdata(pdata); + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + pcim_iounmap_regions(pdev, bar_mask); + + pci_disable_device(pdev); +} + +static int __maybe_unused phytmac_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytmac *pdata = pci_get_drvdata(pdev); + int ret; + + ret = phytmac_drv_suspend(pdata); + + return ret; +} + +static int __maybe_unused phytmac_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytmac *pdata = pci_get_drvdata(pdev); + int ret; + + ret = phytmac_drv_resume(pdata); + + return ret; +} + +struct phytmac_data phytmac_sgmii = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = true, + .interface = PHY_INTERFACE_MODE_SGMII, +}; + +struct phytmac_data phytmac_1000basex = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 1000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_SGMII, + .properties = fl_properties[0], +}; + +struct phytmac_data phytmac_2500basex = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 2500, + .duplex = true, + .interface = PHY_INTERFACE_MODE_2500BASEX, + .properties = fl_properties[1], +}; + +struct phytmac_data phytmac_5000baser = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 5000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_5GBASER, + .properties = fl_properties[2], +}; + +struct phytmac_data phytmac_usxgmii = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 10000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_USXGMII, + .properties = fl_properties[3], +}; + +static const struct pci_device_id phytmac_pci_table[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_SGMII), + .driver_data = (kernel_ulong_t)&phytmac_sgmii}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_1000BASEX), + .driver_data = (kernel_ulong_t)&phytmac_1000basex}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_2500BASEX), + .driver_data = (kernel_ulong_t)&phytmac_2500basex}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_5GBASER), + .driver_data = (kernel_ulong_t)&phytmac_5000baser}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_USXGMII), + .driver_data = (kernel_ulong_t)&phytmac_usxgmii}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_10GBASER), + .driver_data = (kernel_ulong_t)&phytmac_usxgmii}, + /* Last entry must be zero */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, phytmac_pci_table); + +static const struct dev_pm_ops phytmac_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytmac_pci_suspend, phytmac_pci_resume) +}; + +static struct pci_driver phytmac_driver = { + .name = PHYTMAC_DRV_NAME, + .id_table = phytmac_pci_table, + .probe = phytmac_pci_probe, + .remove = phytmac_pci_remove, + .driver = { + .pm = &phytmac_pci_pm_ops, + } +}; + +module_pci_driver(phytmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium NIC PCI wrapper"); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c new file mode 100644 index 00000000000000..305ff5866e2fe0 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium GMAC Platform wrapper. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" + +static const struct phytmac_config phytium_1p0_config = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .tsu_rate = 300000000, +}; + +static const struct phytmac_config phytium_2p0_config = { + .hw_if = &phytmac_2p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_LPI + | PHYTMAC_CAPS_LSO + | PHYTMAC_CAPS_MSG + | PHYTMAC_CAPS_JUMBO, + .queue_num = 2, + .tsu_rate = 300000000, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id phytmac_dt_ids[] = { + { .compatible = "phytium,gmac-1.0", .data = &phytium_1p0_config }, + { .compatible = "phytium,gmac-2.0", .data = &phytium_2p0_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phytmac_dt_ids); +#endif /* CONFIG_OF */ + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytmac_acpi_ids[] = { + { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_1p0_config }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); +#else +#define phytmac_acpi_ids NULL +#endif + +static int phytmac_get_phy_mode(struct platform_device *pdev) +{ + const char *pm; + int err, i; + + err = device_property_read_string(&pdev->dev, "phy-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phy_modes(i))) + return i; + } + + return -ENODEV; +} + +static int phytmac_plat_probe(struct platform_device *pdev) +{ + const struct phytmac_config *phytmac_config = &phytium_1p0_config; + struct device_node *np = pdev->dev.of_node; + struct resource *regs; + struct phytmac *pdata; + int ret, i; + u32 queue_num; + + pdata = phytmac_alloc_pdata(&pdev->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + platform_set_drvdata(pdev, pdata); + + pdata->platdev = pdev; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(phytmac_dt_ids, np); + if (match && match->data) { + phytmac_config = match->data; + pdata->hw_if = phytmac_config->hw_if; + pdata->capacities = phytmac_config->caps; + pdata->queues_max_num = phytmac_config->queue_num; + } + } else if (has_acpi_companion(&pdev->dev)) { + const struct acpi_device_id *match; + + match = acpi_match_device(phytmac_acpi_ids, &pdev->dev); + if (match && match->driver_data) { + phytmac_config = (void *)match->driver_data; + pdata->hw_if = phytmac_config->hw_if; + pdata->capacities = phytmac_config->caps; + pdata->queues_max_num = phytmac_config->queue_num; + } + } + + i = 0; + pdata->mac_regs = devm_platform_get_and_ioremap_resource(pdev, i, ®s); + if (IS_ERR(pdata->mac_regs)) { + dev_err(&pdev->dev, "mac_regs ioremap failed\n"); + ret = PTR_ERR(pdata->mac_regs); + goto err_mem; + } + pdata->ndev->base_addr = regs->start; + + if (pdata->capacities & PHYTMAC_CAPS_MSG) { + ++i; + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (regs) { + pdata->msg_regs = ioremap_wt(regs->start, MEMORY_SIZE); + if (!pdata->msg_regs) { + dev_err(&pdev->dev, "msg_regs ioremap failed, i=%d\n", i); + goto err_mem; + } + } + } + + if (device_property_read_bool(&pdev->dev, "lpi")) + pdata->capacities |= PHYTMAC_CAPS_LPI; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + /* lpi resource */ + ++i; + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (regs) { + pdata->mhu_regs = ioremap(regs->start, MHU_SIZE); + if (!pdata->mhu_regs) + dev_err(&pdev->dev, "mhu_regs ioremap failed, i=%d\n", i); + } + } + + if (device_property_read_u32(&pdev->dev, "dma-burst-length", &pdata->dma_burst_length)) + pdata->dma_burst_length = DEFAULT_DMA_BURST_LENGTH; + + if (device_property_read_u32(&pdev->dev, "jumbo-max-length", &pdata->jumbo_len)) + pdata->jumbo_len = DEFAULT_JUMBO_MAX_LENGTH; + + if (device_property_read_u32(&pdev->dev, "queue-number", &queue_num)) + pdata->queues_num = pdata->queues_max_num; + else + pdata->queues_num = queue_num; + + pdata->wol = 0; + if (device_property_read_bool(&pdev->dev, "magic-packet")) + pdata->wol |= PHYTMAC_WAKE_MAGIC; + + pdata->use_ncsi = device_property_read_bool(&pdev->dev, "use-ncsi"); + pdata->use_mii = device_property_read_bool(&pdev->dev, "use-mii"); + + pdata->power_state = PHYTMAC_POWEROFF; + + device_set_wakeup_capable(&pdev->dev, pdata->wol & PHYTMAC_WOL_MAGIC_PACKET); + + for (i = 0; i < pdata->queues_num; i++) { + pdata->irq_type = IRQ_TYPE_INT; + pdata->queue_irq[i] = platform_get_irq(pdev, i); + } + + ret = phytmac_get_phy_mode(pdev); + if (ret < 0) + pdata->phy_interface = PHY_INTERFACE_MODE_MII; + else + pdata->phy_interface = ret; + + ret = phytmac_drv_probe(pdata); + if (ret) + goto err_mem; + + if (netif_msg_probe(pdata)) { + dev_notice(&pdev->dev, "phytium net device enabled\n"); + dev_dbg(pdata->dev, "use_ncsi:%d, use_mii:%d, wol:%d, queues_num:%d\n", + pdata->use_ncsi, pdata->use_mii, pdata->wol, pdata->queues_num); + } + + return 0; + +err_mem: + phytmac_free_pdata(pdata); + +err_alloc: + dev_err(&pdev->dev, "phytium net device not enabled\n"); + + return ret; +} + +static int phytmac_plat_remove(struct platform_device *pdev) +{ + struct phytmac *pdata = platform_get_drvdata(pdev); + + phytmac_drv_remove(pdata); + phytmac_free_pdata(pdata); + + return 0; +} + +static int __maybe_unused phytmac_plat_suspend(struct device *dev) +{ + struct phytmac *pdata = dev_get_drvdata(dev); + int ret; + + ret = phytmac_drv_suspend(pdata); + + return ret; +} + +static int __maybe_unused phytmac_plat_resume(struct device *dev) +{ + struct phytmac *pdata = dev_get_drvdata(dev); + int ret; + + ret = phytmac_drv_resume(pdata); + + return ret; +} + +static const struct dev_pm_ops phytmac_plat_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytmac_plat_suspend, phytmac_plat_resume) +}; + +static struct platform_driver phytmac_driver = { + .probe = phytmac_plat_probe, + .remove = phytmac_plat_remove, + .driver = { + .name = PHYTMAC_DRV_NAME, + .of_match_table = of_match_ptr(phytmac_dt_ids), + .acpi_match_table = phytmac_acpi_ids, + .pm = &phytmac_plat_pm_ops, + }, +}; + +module_platform_driver(phytmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium Ethernet driver"); +MODULE_AUTHOR("Wenting Song"); +MODULE_ALIAS("platform:phytmac"); diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.c b/drivers/net/ethernet/phytium/phytmac_ptp.c new file mode 100644 index 00000000000000..26b1b75edbde1d --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ptp.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * 1588 PTP support for Phytium GMAC device. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_ptp.h" + +bool phytmac_ptp_one_step(struct sk_buff *skb) +{ + struct ptp_header *hdr; + unsigned int ptp_class; + u8 msgtype; + + /* No need to parse packet if PTP TS is not involved */ + if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) + goto not_oss; + + /* Identify and return whether PTP one step sync is being processed */ + ptp_class = ptp_classify_raw(skb); + if (ptp_class == PTP_CLASS_NONE) + goto not_oss; + + hdr = ptp_parse_header(skb, ptp_class); + if (!hdr) + goto not_oss; + + if (hdr->flag_field[0] & 0x2) + goto not_oss; + + msgtype = ptp_get_msgtype(hdr, ptp_class); + if (msgtype == PTP_MSGTYPE_SYNC) + return true; + +not_oss: + return false; +} + +int phytmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + spin_lock_irqsave(&pdata->ts_clk_lock, flags); + hw_if->get_time(pdata, ts); + spin_unlock_irqrestore(&pdata->ts_clk_lock, flags); + + return 0; +} + +int phytmac_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + spin_lock_irqsave(&pdata->ts_clk_lock, flags); + hw_if->set_time(pdata, ts->tv_sec, ts->tv_nsec); + spin_unlock_irqrestore(&pdata->ts_clk_lock, flags); + + return 0; +} + +int phytmac_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + bool negative = false; + + if (scaled_ppm < 0) { + negative = true; + scaled_ppm = -scaled_ppm; + } + + hw_if->adjust_fine(pdata, scaled_ppm, negative); + return 0; +} + +int phytmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + int negative = 0; + + if (delta < 0) { + negative = 1; + delta = -delta; + } + + spin_lock_irq(&pdata->ts_clk_lock); + hw_if->adjust_time(pdata, delta, negative); + spin_unlock_irq(&pdata->ts_clk_lock); + + return 0; +} + +int phytmac_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +void phytmac_ptp_init_timer(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 rem = 0; + u64 adj; + + pdata->ts_rate = hw_if->get_ts_rate(pdata); + pdata->ts_incr.ns = div_u64_rem(NSEC_PER_SEC, pdata->ts_rate, &rem); + if (rem) { + adj = rem; + adj <<= 24; + pdata->ts_incr.sub_ns = div_u64(adj, pdata->ts_rate); + } else { + pdata->ts_incr.sub_ns = 0; + } +} + +void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct timespec64 ts; + + if (hw_if->ts_valid(pdata, desc, PHYTMAC_RX)) { + hw_if->get_timestamp(pdata, desc->desc4, desc->desc5, &ts); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + } +} + +int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct timespec64 ts; + struct skb_shared_hwtstamps shhwtstamps; + + if (queue->pdata->ts_config.tx_type == TS_DISABLED) + return -EOPNOTSUPP; + + if (!hw_if->ts_valid(pdata, desc, PHYTMAC_TX)) + return -EINVAL; + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + hw_if->get_timestamp(pdata, desc->desc4, desc->desc5, &ts); + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb, &shhwtstamps); + + return 0; +} + +int phytmac_ptp_register(struct phytmac *pdata) +{ + pdata->ptp_clock_info.owner = THIS_MODULE; + snprintf(pdata->ptp_clock_info.name, 16, "%s", pdata->ndev->name); + pdata->ptp_clock_info.max_adj = 64000000; /* In PPB */ + pdata->ptp_clock_info.n_alarm = 0; + pdata->ptp_clock_info.n_ext_ts = 0; + pdata->ptp_clock_info.n_per_out = 0; + pdata->ptp_clock_info.pps = 1; + pdata->ptp_clock_info.adjfine = phytmac_ptp_adjfine; + pdata->ptp_clock_info.adjtime = phytmac_ptp_adjtime; + pdata->ptp_clock_info.gettime64 = phytmac_ptp_gettime; + pdata->ptp_clock_info.settime64 = phytmac_ptp_settime; + pdata->ptp_clock_info.enable = phytmac_ptp_enable; + pdata->ptp_clock = ptp_clock_register(&pdata->ptp_clock_info, pdata->dev); + if (IS_ERR_OR_NULL(pdata->ptp_clock)) { + dev_err(pdata->dev, "ptp_clock_register failed %lu\n", + PTR_ERR(pdata->ptp_clock)); + return -EINVAL; + } + + return 0; +} + +void phytmac_ptp_unregister(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pdata->ptp_clock) + ptp_clock_unregister(pdata->ptp_clock); + pdata->ptp_clock = NULL; + + hw_if->clear_time(pdata); + + dev_info(pdata->dev, "phytmac ptp clock unregistered.\n"); +} + +void phytmac_ptp_init(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + phytmac_ptp_init_timer(pdata); + + hw_if->init_ts_hw(pdata); + + dev_info(pdata->dev, "phytmac ptp clock init success.\n"); +} + +int phytmac_ptp_get_ts_config(struct net_device *dev, struct ifreq *rq) +{ + struct hwtstamp_config *tstamp_config; + struct phytmac *pdata = netdev_priv(dev); + + if (!IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + return -EOPNOTSUPP; + + tstamp_config = &pdata->ts_config; + + if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) + return -EFAULT; + else + return 0; +} + +int phytmac_ptp_set_ts_config(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct hwtstamp_config config; + struct phytmac *pdata = netdev_priv(dev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct ts_ctrl tstamp_ctrl; + int ret; + + memset(&tstamp_ctrl, 0, sizeof(struct ts_ctrl)); + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + tstamp_ctrl.one_step = 1; + tstamp_ctrl.tx_control = TS_ALL_FRAMES; + break; + case HWTSTAMP_TX_ON: + tstamp_ctrl.one_step = 0; + tstamp_ctrl.tx_control = TS_ALL_FRAMES; + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + tstamp_ctrl.rx_control = TS_ALL_PTP_FRAMES; + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_ALL: + tstamp_ctrl.rx_control = TS_ALL_FRAMES; + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + config.rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + ret = hw_if->set_ts_config(pdata, &tstamp_ctrl); + if (ret) + return ret; + + /* save these settings for future reference */ + pdata->ts_config = config; + memcpy(&pdata->ts_config, &config, sizeof(config)); + + if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) + return -EFAULT; + else + return 0; +} + diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.h b/drivers/net/ethernet/phytium/phytmac_ptp.h new file mode 100644 index 00000000000000..72c8b7c6741370 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ptp.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Phytium Ethernet Controller driver + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _PHYTMAC_PTP_H +#define _PHYTMAC_PTP_H + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP +bool phytmac_ptp_one_step(struct sk_buff *skb); +int phytmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts); +int phytmac_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts); +int phytmac_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm); +int phytmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta); +int phytmac_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on); +void phytmac_ptp_init_timer(struct phytmac *pdata); +void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc); +int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc); +int phytmac_ptp_register(struct phytmac *pdata); +void phytmac_ptp_unregister(struct phytmac *pdata); +void phytmac_ptp_init(struct net_device *ndev); +int phytmac_ptp_get_ts_config(struct net_device *dev, struct ifreq *rq); +int phytmac_ptp_set_ts_config(struct net_device *dev, struct ifreq *ifr, int cmd); +#else +static inline bool phytmac_ptp_one_step(struct sk_buff *skb) +{ + return 1; +} + +static inline void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc) {} +static inline int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + return -1; +} + +static inline int phytmac_ptp_register(struct phytmac *pdata) +{ + return 0; +} + +static inline void phytmac_ptp_unregister(struct phytmac *pdata) {} +static inline void phytmac_ptp_init(struct net_device *ndev) {} + +#endif +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c new file mode 100644 index 00000000000000..2d9f67c82050c9 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -0,0 +1,1370 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" + +static int phytmac_enable_promise(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(PROMISC); + else + value &= ~PHYTMAC_BIT(PROMISC); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + + return 0; +} + +static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +{ + u32 config; + + if (enable) { + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, -1); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config |= PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, 0); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config &= ~PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + } + + return 0; +} + +static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +{ + u32 config; + + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, mc_filter[0]); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, mc_filter[1]); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config |= PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + return 0; +} + +static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(RCO_EN); + else + value &= ~PHYTMAC_BIT(RCO_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + + return 0; +} + +static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + + if (enable) + value |= PHYTMAC_BIT(TCO_EN); + else + value &= ~PHYTMAC_BIT(TCO_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, value); + + return 0; +} + +static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + + if (enable) + value |= PHYTMAC_BIT(MPE); + else + value &= ~PHYTMAC_BIT(MPE); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, value); + + return 0; +} + +static int phytmac_enable_pause(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(PAUSE_EN); + else + value &= ~PHYTMAC_BIT(PAUSE_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + return 0; +} + +static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +{ + u32 old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + u32 ctrl; + + ctrl = old_ctrl; + + if (rx_tx & PHYTMAC_TX) { + if (enable) + ctrl |= PHYTMAC_BIT(TE); + else + ctrl &= ~PHYTMAC_BIT(TE); + } + + if (rx_tx & PHYTMAC_RX) { + if (enable) + ctrl |= PHYTMAC_BIT(RE); + else + ctrl &= ~PHYTMAC_BIT(RE); + } + + if (ctrl ^ old_ctrl) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + return 0; +} + +static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + + if (enable) + value |= PHYTMAC_BIT(AUTONEG); + else + value &= ~PHYTMAC_BIT(AUTONEG); + + PHYTMAC_WRITE(pdata, PHYTMAC_PCSCTRL, value); + + return 0; +} + +static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + u32 ctrl, config; + + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD)); + + if (speed == SPEED_100) + config |= PHYTMAC_BIT(SPEED); + else if (speed == SPEED_1000 || speed == SPEED_2500) + config |= PHYTMAC_BIT(GM_EN); + + if (duplex) + config |= PHYTMAC_BIT(FD); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + if (speed == SPEED_2500) { + ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + ctrl |= PHYTMAC_BIT(2PT5G); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + } + + if (speed == SPEED_10000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_10000M); + else if (speed == SPEED_5000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_5000M); + else if (speed == SPEED_2500) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_2500M); + else if (speed == SPEED_1000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_1000M); + else + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + + return 0; +} + +static int phytmac_mac_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + u32 config; + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) { + config = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + if (speed == SPEED_10000) { + config = PHYTMAC_SET_BITS(config, SERDES_RATE, PHYTMAC_SERDES_RATE_10G); + config = PHYTMAC_SET_BITS(config, USX_SPEED, PHYTMAC_SPEED_10000M); + } else if (speed == SPEED_5000) { + config = PHYTMAC_SET_BITS(config, SERDES_RATE, PHYTMAC_SERDES_RATE_5G); + config = PHYTMAC_SET_BITS(config, USX_SPEED, PHYTMAC_SPEED_5000M); + } + + /* reset */ + config &= ~(PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN)); + config |= PHYTMAC_BIT(RX_SYNC_RESET); + + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, config); + + /* enable rx and tx */ + config &= ~(PHYTMAC_BIT(RX_SYNC_RESET)); + config |= PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, config); + } + + return 0; +} + +static int phytmac_pcs_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +{ + u32 bottom; + u16 top; + + bottom = PHYTMAC_READ(pdata, PHYTMAC_MAC1B); + top = PHYTMAC_READ(pdata, PHYTMAC_MAC1T); + + addr[0] = bottom & 0xff; + addr[1] = (bottom >> 8) & 0xff; + addr[2] = (bottom >> 16) & 0xff; + addr[3] = (bottom >> 24) & 0xff; + addr[4] = top & 0xff; + addr[5] = (top >> 8) & 0xff; + + return 0; +} + +static int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +{ + u32 bottom; + u16 top; + + bottom = cpu_to_le32(*((u32 *)addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_MAC1B, bottom); + top = cpu_to_le16(*((u16 *)(addr + 4))); + PHYTMAC_WRITE(pdata, PHYTMAC_MAC1T, top); + + return 0; +} + +static void phytmac_reset_hw(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q; + u32 ctrl; + + ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + + ctrl &= ~(PHYTMAC_BIT(RE) | PHYTMAC_BIT(TE)); + ctrl |= PHYTMAC_BIT(CLEARSTAT); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + /* Disable and clear all interrupts and disable queues */ + for (q = 0, queue = pdata->queues; q < pdata->queues_max_num; ++q, ++queue) { + if (q == 0) { + PHYTMAC_WRITE(pdata, PHYTMAC_ID, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_IS, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR_Q0, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR_Q0, 1); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_IDR(q - 1), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_ISR(q - 1), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR(q - 1), 1); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR(q - 1), 1); + } + + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTRH(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), 0); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), 0); + } +} + +static void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +{ + reg_buff[0] = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + reg_buff[1] = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + reg_buff[2] = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + reg_buff[3] = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + reg_buff[4] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR_Q0); + reg_buff[5] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR_Q0); + reg_buff[6] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(1)); + reg_buff[7] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(1)); + reg_buff[8] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(2)); + reg_buff[9] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(2)); + reg_buff[10] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(3)); + reg_buff[11] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(3)); + reg_buff[12] = PHYTMAC_READ(pdata, PHYTMAC_HCONFIG); + reg_buff[13] = PHYTMAC_READ(pdata, PHYTMAC_IM); + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + reg_buff[14] = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + reg_buff[15] = PHYTMAC_READ(pdata, PHYTMAC_USXSTATUS); + } else { + reg_buff[14] = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + reg_buff[15] = PHYTMAC_READ(pdata, PHYTMAC_PCSSTATUS); + } +} + +static int phytmac_init_hw(struct phytmac *pdata) +{ + u32 config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + u32 dmaconfig; + u32 nctrlconfig; + + nctrlconfig = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + nctrlconfig |= PHYTMAC_BIT(MPE); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, nctrlconfig); + + phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + + PHYTMAC_WRITE(pdata, PHYTMAC_AXICTRL, 0x1010); + + /* jumbo */ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + config |= PHYTMAC_BIT(JUMBO_EN); + else + config |= PHYTMAC_BIT(RCV_BIG); + /* promisc */ + if (pdata->ndev->flags & IFF_PROMISC) + config |= PHYTMAC_BIT(PROMISC); + if (pdata->ndev->features & NETIF_F_RXCSUM) + config |= PHYTMAC_BIT(RCO_EN); + if (!(pdata->ndev->flags & IFF_BROADCAST)) + config |= PHYTMAC_BIT(NO_BCAST); + /* pause enable */ + config |= PHYTMAC_BIT(PAUSE_EN); + /* Rx Fcs remove */ + config |= PHYTMAC_BIT(FCS_REMOVE); + if (pdata->dma_data_width == PHYTMAC_DBW_64) + config |= PHYTMAC_BIT(DBW64); + if (pdata->dma_data_width == PHYTMAC_DBW_128) + config |= PHYTMAC_BIT(DBW128); + /* mdc div */ + config = PHYTMAC_SET_BITS(config, MCD, 6); + netdev_dbg(pdata->ndev, "phytmac configure NetConfig with 0x%08x\n", + config); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + /* init dma */ + dmaconfig = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + if (pdata->dma_burst_length) + dmaconfig = PHYTMAC_SET_BITS(dmaconfig, BURST, pdata->dma_burst_length); + /* default in small endian */ + dmaconfig &= ~(PHYTMAC_BIT(ENDIA_PKT) | PHYTMAC_BIT(ENDIA_DESC)); + + if (pdata->ndev->features & NETIF_F_HW_CSUM) + dmaconfig |= PHYTMAC_BIT(TCO_EN); + else + dmaconfig &= ~PHYTMAC_BIT(TCO_EN); + + if (pdata->dma_addr_width) + dmaconfig |= PHYTMAC_BIT(ABW); + + /* fdir ethtype -- ipv4 */ + PHYTMAC_WRITE(pdata, PHYTMAC_ETHT(0), (uint16_t)ETH_P_IP); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + dmaconfig |= PHYTMAC_BIT(RX_EXBD_EN) | PHYTMAC_BIT(TX_EXBD_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + return 0; +} + +static int phytmac_powerup_hw(struct phytmac *pdata, int on) +{ + u32 status, data0, data1, rdata1; + int ret; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh status is busy, status=%x\n", status); + + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy, data0=%x\n", data0); + + data0 = 0; + data0 = PHYTMAC_SET_BITS(data0, DATA0_MSG, PHYTMAC_MSG_PM); + data0 = PHYTMAC_SET_BITS(data0, DATA0_PRO, PHYTMAC_PRO_ID); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA0, data0); + data1 = 0; + + if (on == PHYTMAC_POWERON) { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATON); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } else { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATOFF); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } + + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_AP_CPP_SET, 1); + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy"); + + rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); + if (rdata1 == data1) + netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + else + netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + } + pdata->power_state = on; + + return 0; +} + +static int phytmac_set_wake(struct phytmac *pdata, int wake) +{ + u32 value = 0; + + if (wake & PHYTMAC_WAKE_MAGIC) + value |= PHYTMAC_BIT(MAGIC); + if (wake & PHYTMAC_WAKE_ARP) + value |= PHYTMAC_BIT(ARP); + if (wake & PHYTMAC_WAKE_UCAST) + value |= PHYTMAC_BIT(UCAST); + if (wake & PHYTMAC_WAKE_MCAST) + value |= PHYTMAC_BIT(MCAST); + + PHYTMAC_WRITE(pdata, PHYTMAC_WOL, value); + + return 0; +} + +static void phytmac_mdio_idle(struct phytmac *pdata) +{ + u32 val; + + /* wait for end of transfer */ + val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + while (!(val & PHYTMAC_BIT(MDIO_IDLE))) { + cpu_relax(); + val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + } +} + +static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C22) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C22_READ) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, regnum) + | PHYTMAC_BITS(MUST, 2))); + + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDATA) & 0xffff; + phytmac_mdio_idle(pdata); + + return data; +} + +static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C22) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C22_WRITE) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, regnum) + | PHYTMAC_BITS(DATA, data) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_ADDR) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_READ) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDATA) & 0xffff; + phytmac_mdio_idle(pdata); + + return data; +} + +static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_ADDR) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_WRITE) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, data) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_get_feature_all(struct phytmac *pdata) +{ + unsigned int queue_mask; + unsigned int num_queues; + int val; + + /* get max queues */ + queue_mask = 0x1; + queue_mask |= PHYTMAC_READ(pdata, PHYTMAC_DEFAULT2) & 0xff; + num_queues = hweight32(queue_mask); + pdata->queues_max_num = num_queues; + + /* get dma desc prefetch number */ + val = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT4, TXDESCRD); + if (val) + pdata->tx_bd_prefetch = (2 << (val - 1)) * + sizeof(struct phytmac_dma_desc); + + val = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT4, RXDESCRD); + if (val) + pdata->rx_bd_prefetch = (2 << (val - 1)) * + sizeof(struct phytmac_dma_desc); + + /* dma bus width */ + pdata->dma_data_width = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT1, DBW); + + /* dma addr width */ + if (PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT2, DAW64)) + pdata->dma_addr_width = 64; + else + pdata->dma_addr_width = 32; + + /* max rx fs */ + pdata->max_rx_fs = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT3, SCR2CMP); + + if (netif_msg_hw(pdata)) + netdev_info(pdata->ndev, "get feature queue_num=%d, daw=%d, dbw=%d, rx_fs=%d, rx_bd=%d, tx_bd=%d\n", + pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, + pdata->max_rx_fs, pdata->rx_bd_prefetch, pdata->tx_bd_prefetch); + return 0; +} + +static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; + u16 index = rx_flow->location; + u32 tmp, fdir_ctrl; + bool ipsrc = false; + bool ipdst = false; + bool port = false; + + tp4sp_v = &rx_flow->h_u.tcp_ip4_spec; + tp4sp_m = &rx_flow->m_u.tcp_ip4_spec; + + if (tp4sp_m->ip4src == 0xFFFFFFFF) { + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, ETHTYPE_SIP_OFFSET); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L2HEAD); + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index), tp4sp_v->ip4src); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index), tmp); + ipsrc = true; + } + + if (tp4sp_m->ip4dst == 0xFFFFFFFF) { + /* 2nd compare reg - IP destination address */ + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, ETHTYPE_DIP_OFFSET); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L2HEAD); + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 1), tp4sp_v->ip4dst); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + 1), tmp); + ipdst = true; + } + + if (tp4sp_m->psrc == 0xFFFF || tp4sp_m->pdst == 0xFFFF) { + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L3HEAD); + if (tp4sp_m->psrc == tp4sp_m->pdst) { + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_SPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_v->psrc | (u32)(tp4sp_v->pdst << 16)); + } else { + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 0); + if (tp4sp_m->psrc == 0xFFFF) { /* src port */ + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_SPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_m->psrc | (u32)(tp4sp_v->psrc << 16)); + } else { /* dst port */ + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_DPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_m->pdst | (u32)(tp4sp_v->pdst << 16)); + } + } + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + 2), tmp); + port = true; + } + + fdir_ctrl = PHYTMAC_READ(pdata, PHYTMAC_FDIR(rx_flow->location)); + + if (ipsrc) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CA, 3 * index); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CA_EN, 1); + } + + if (ipdst) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CB, 3 * index + 1); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CB_EN, 1); + } + + if (port) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CC, 3 * index + 2); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CC_EN, 1); + } + + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, QUEUE_NUM, (rx_flow->ring_cookie) & 0xFF); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, ETH_TYPE, 0); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, ETH_EN, 1); + + PHYTMAC_WRITE(pdata, PHYTMAC_FDIR(rx_flow->location), fdir_ctrl); + + return 0; +} + +static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + int i; + int index = rx_flow->location; + + PHYTMAC_WRITE(pdata, PHYTMAC_FDIR(index), 0); + for (i = 0; i < 3; i++) { + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + i), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + i), 0); + } + return 0; +} + +static int phytmac_init_ring_hw(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q = 0; + u32 buffer_size = pdata->rx_buffer_len / 64; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + if (q == 0) { + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR_Q0, + lower_32_bits(queue->rx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR_Q0, + lower_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, + PHYTMAC_SET_BITS(PHYTMAC_READ(pdata, PHYTMAC_DCONFIG), + RX_BUF_LEN, buffer_size)); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR(q - 1), + lower_32_bits(queue->rx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR(q - 1), + lower_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_RBQS(q - 1), buffer_size); + } + + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTRH(q), upper_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), upper_32_bits(queue->rx_ring_addr)); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), queue->tx_tail); + } + + return 0; +} + +static u32 phytmac_get_irq_mask(u32 mask) +{ + u32 value = 0; + + value |= (mask & PHYTMAC_INT_TX_COMPLETE) ? PHYTMAC_BIT(TXCOMP) : 0; + value |= (mask & PHYTMAC_INT_TX_ERR) ? PHYTMAC_BIT(BUS_ERR) : 0; + value |= (mask & PHYTMAC_INT_RX_COMPLETE) ? PHYTMAC_BIT(RXCOMP) : 0; + value |= (mask & PHYTMAC_INT_RX_OVERRUN) ? PHYTMAC_BIT(RXOVERRUN) : 0; + value |= (mask & PHYTMAC_INT_RX_DESC_FULL) ? PHYTMAC_BIT(RUB) : 0; + + return value; +} + +static u32 phytmac_get_irq_status(u32 value) +{ + u32 status = 0; + + status |= (value & PHYTMAC_BIT(TXCOMP)) ? PHYTMAC_INT_TX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(BUS_ERR)) ? PHYTMAC_INT_TX_ERR : 0; + status |= (value & PHYTMAC_BIT(RXCOMP)) ? PHYTMAC_INT_RX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(RXOVERRUN)) ? PHYTMAC_INT_RX_OVERRUN : 0; + status |= (value & PHYTMAC_BIT(RUB)) ? PHYTMAC_INT_RX_DESC_FULL : 0; + + return status; +} + +static void phytmac_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_IE, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_IER(queue_index - 1), value); +} + +static void phytmac_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_ID, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_IDR(queue_index - 1), value); +} + +static void phytmac_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_IS, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_ISR(queue_index - 1), value); +} + +static unsigned int phytmac_get_intx_mask(struct phytmac *pdata) +{ + u32 value; + + value = PHYTMAC_READ(pdata, PHYTMAC_INTX_IRQ_MASK); + PHYTMAC_WRITE(pdata, PHYTMAC_INTX_IRQ_MASK, value); + + return value; +} + +static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +{ + u32 status, value; + + if (queue_index == 0) + value = PHYTMAC_READ(pdata, PHYTMAC_IS); + else + value = PHYTMAC_READ(pdata, PHYTMAC_ISR(queue_index - 1)); + + status = phytmac_get_irq_status(value); + + return status; +} + +static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) +{ + unsigned int i, ctrl; + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + struct phytmac_tx_skb *tx_skb; + unsigned int eof = 1; + + i = tx_tail; + + if (!(pdata->capacities & PHYTMAC_CAPS_TAILPTR)) { + ctrl = PHYTMAC_BIT(TX_USED); + desc = phytmac_get_tx_desc(queue, i); + desc->desc1 = ctrl; + } + + do { + i--; + tx_skb = phytmac_get_tx_skb(queue, i); + desc = phytmac_get_tx_desc(queue, i); + + ctrl = (u32)tx_skb->length; + if (eof) { + ctrl |= PHYTMAC_BIT(TX_LAST); + eof = 0; + } + + if (unlikely(i == (pdata->tx_ring_size - 1))) + ctrl |= PHYTMAC_BIT(TX_WRAP); + + if (i == queue->tx_tail) { + ctrl |= PHYTMAC_BITS(TX_LSO, packet->lso); + ctrl |= PHYTMAC_BITS(TX_TCP_SEQ_SRC, packet->seq); + if (packet->nocrc) + ctrl |= PHYTMAC_BIT(TX_NOCRC); + } else { + ctrl |= PHYTMAC_BITS(MSS_MFS, packet->mss); + } + + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* Make newly descriptor visible to hardware */ + wmb(); + desc->desc1 = ctrl; + } while (i != queue->tx_tail); + + return 0; +} + +static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) +{ + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 |= PHYTMAC_BIT(RX_USED); +} + +static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, + u32 index, dma_addr_t addr) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + if (addr) { + if (unlikely(index == (pdata->rx_ring_size - 1))) + addr |= PHYTMAC_BIT(RX_WRAP); + desc->desc1 = 0; + desc->desc2 = upper_32_bits(addr); + desc->desc0 = lower_32_bits(addr) | PHYTMAC_BIT(RX_USED); + } + return 0; +} + +static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count) +{ + struct phytmac_dma_desc *desc; + u32 index = queue->rx_head + count - 1; + + while (count) { + desc = phytmac_get_rx_desc(queue, index); + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + dma_wmb(); + index--; + count--; + } + + return 0; +} + +static void phytmac_tx_start(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), + BIT(31) | queue->tx_tail); + + if (pdata->capacities & PHYTMAC_CAPS_START) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); +} + +static void phytmac_restart(struct phytmac *pdata) +{ + int q; + struct phytmac_queue *queue; + + for (q = 0; q < pdata->queues_num; q++) { + queue = &pdata->queues[q]; + if (queue->tx_head != queue->tx_tail) { + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); + break; + } + } +} + +static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc1, TX_USED); +} + +static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +{ + return (desc->desc0 & PHYTMAC_BIT(RX_USED)) != 0; +} + +static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +{ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + return desc->desc1 & PHYTMAC_JUMBO_FRAME_MASK; + else + return desc->desc1 & PHYTMAC_FRAME_MASK; +} + +static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) +{ + dma_addr_t addr = 0; + + addr = ((u64)(desc->desc2) << 32); + + addr |= (desc->desc0 & 0xfffffffc); + return addr; +} + +static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + u32 check = value >> PHYTMAC_RX_CSUM_INDEX & 0x3; + + return (check == PHYTMAC_RX_CSUM_IP_TCP || check == PHYTMAC_RX_CSUM_IP_UDP); +} + +static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return ((value & PHYTMAC_BIT(RX_SOF)) && (value & PHYTMAC_BIT(RX_EOF))); +} + +static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RX_SOF)); +} + +static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RX_EOF)); +} + +static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +{ + unsigned int frag; + unsigned int tmp = end; + struct phytmac_dma_desc *desc; + + if (begin > end) + tmp = end + queue->pdata->rx_ring_size; + + for (frag = begin; frag != end; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + } +} + +static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) +{ + u32 old_ctrl, old_config; + u32 ctrl, config, usxctl; + + old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + old_config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + ctrl = old_ctrl; + config = old_config; + + if (state->speed == SPEED_10000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_10000M); + else if (state->speed == SPEED_5000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_5000M); + else if (state->speed == SPEED_2500) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_2500M); + else if (state->speed == SPEED_1000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_1000M); + else if (state->speed == SPEED_100 || state->speed == SPEED_10) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + + config &= ~(PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN) + | PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); + ctrl &= ~(PHYTMAC_BIT(HIGHSPEED) | PHYTMAC_BIT(2PT5G)); + + if (state->interface == PHY_INTERFACE_MODE_SGMII || + state->interface == PHY_INTERFACE_MODE_2500BASEX) { + config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); + } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || + state->interface == PHY_INTERFACE_MODE_USXGMII || + state->interface == PHY_INTERFACE_MODE_5GBASER) { + usxctl = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + if (state->speed == SPEED_10000) { + usxctl = PHYTMAC_SET_BITS(usxctl, SERDES_RATE, PHYTMAC_SERDES_RATE_10G); + usxctl = PHYTMAC_SET_BITS(usxctl, USX_SPEED, PHYTMAC_SPEED_10000M); + } else if (state->speed == SPEED_5000) { + usxctl = PHYTMAC_SET_BITS(usxctl, SERDES_RATE, PHYTMAC_SERDES_RATE_5G); + usxctl = PHYTMAC_SET_BITS(usxctl, USX_SPEED, PHYTMAC_SPEED_5000M); + } + usxctl |= PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, usxctl); + + config |= PHYTMAC_BIT(PCS_EN); + ctrl |= PHYTMAC_BIT(HIGHSPEED); + } + + if (state->duplex) + config |= PHYTMAC_BIT(FD); + + if (old_ctrl ^ ctrl) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + if (old_config ^ config) + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ + if (state->interface == PHY_INTERFACE_MODE_SGMII || + state->interface == PHY_INTERFACE_MODE_2500BASEX) + phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); +} + +static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, + phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_2500BASEX) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_NSTATUS, PCS_LINK); + else if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_USXSTATUS, USX_PCS_LINK); + + return 0; +} + +static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc = NULL; + int i; + + for (i = 0; i < queue->pdata->tx_ring_size; i++) { + desc = phytmac_get_tx_desc(queue, i); + desc->desc2 = 0; + desc->desc0 = 0; + /* make newly desc1 to hardware */ + wmb(); + desc->desc1 = PHYTMAC_BIT(TX_USED); + } + desc->desc1 |= PHYTMAC_BIT(TX_WRAP); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), queue->tx_tail); +} + +static void phytmac_get_hw_stats(struct phytmac *pdata) +{ + u32 stats[45]; + int i, j; + u64 val; + u64 *p = &pdata->stats.tx_octets; + + for (i = 0 ; i < 45; i++) + stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCTTX + i * 4); + + for (i = 0, j = 0; i < 45; i++) { + if (i == 0 || i == 20) { + val = (u64)stats[i + 1] << 32 | stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } else { + if (i != 1 && i != 21) { + val = stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } + } + } +} + +static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +{ + u32 ns, secl, sech; + + ns = PHYTMAC_READ(pdata, PHYTMAC_TN); + secl = PHYTMAC_READ(pdata, PHYTMAC_TSL); + sech = PHYTMAC_READ(pdata, PHYTMAC_TSH); + + ts->tv_nsec = ns; + ts->tv_sec = (((u64)sech << 32) | secl) & SEC_MAX_VAL; +} + +static void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +{ + u32 secl, sech; + + secl = (u32)sec; + sech = (sec >> 32) & (0xffff); + + PHYTMAC_WRITE(pdata, PHYTMAC_TN, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TSH, sech); + PHYTMAC_WRITE(pdata, PHYTMAC_TSL, secl); + PHYTMAC_WRITE(pdata, PHYTMAC_TN, nsec); +} + +static void phytmac_clear_time(struct phytmac *pdata) +{ + u32 value; + + pdata->ts_incr.sub_ns = 0; + pdata->ts_incr.ns = 0; + + value = PHYTMAC_READ(pdata, PHYTMAC_TISN); + value = PHYTMAC_SET_BITS(value, SUB_NSECL, 0); + value = PHYTMAC_SET_BITS(value, SUB_NSECH, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TISN, value); + + value = PHYTMAC_READ(pdata, PHYTMAC_TI); + value = PHYTMAC_SET_BITS(value, INCR_NS, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TI, value); + + PHYTMAC_WRITE(pdata, PHYTMAC_TA, 0); +} + +static int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +{ + if (ctrl->rx_control == TS_ALL_PTP_FRAMES) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(STORE_RX_TS)); + + PHYTMAC_WRITE(pdata, PHYTMAC_TXBDCTRL, PHYTMAC_BITS(TX_TSMODE, ctrl->tx_control)); + PHYTMAC_WRITE(pdata, PHYTMAC_RXBDCTRL, PHYTMAC_BITS(RX_TSMODE, ctrl->rx_control)); + + return 0; +} + +static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +{ + u32 value; + + value = PHYTMAC_BITS(SUB_NSECL, incr->sub_ns) | + PHYTMAC_BITS(SUB_NSECH, incr->sub_ns >> 8); + PHYTMAC_WRITE(pdata, PHYTMAC_TISN, value); + PHYTMAC_WRITE(pdata, PHYTMAC_TI, incr->ns); + + return 0; +} + +static void phytmac_ptp_init_hw(struct phytmac *pdata) +{ + struct timespec64 ts; + + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); + phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + + phytmac_set_tsincr(pdata, &pdata->ts_incr); +} + +static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +{ + struct ts_incr ts_incr; + u32 tmp; + u64 adj; + + ts_incr.ns = pdata->ts_incr.ns; + ts_incr.sub_ns = pdata->ts_incr.sub_ns; + + tmp = ((u64)ts_incr.ns << PHYTMAC_SUB_NSECL_INDEX) + ts_incr.sub_ns; + adj = ((u64)ppm * tmp + (USEC_PER_SEC >> 1)) >> PHYTMAC_SUB_NSECL_INDEX; + + adj = div_u64(adj, USEC_PER_SEC); + adj = negative ? (tmp - adj) : (tmp + adj); + + ts_incr.ns = (adj >> PHYTMAC_SUB_NSEC_WIDTH) + & ((1 << PHYTMAC_SUB_NSECL_WIDTH) - 1); + ts_incr.sub_ns = adj & ((1 << PHYTMAC_SUB_NSEC_WIDTH) - 1); + + phytmac_set_tsincr(pdata, &ts_incr); + + return 0; +} + +static int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +{ + u32 adj; + + if (delta > PHYTMAC_ADJUST_SEC_MAX) { + struct timespec64 now, then; + + if (neg) + then = ns_to_timespec64(-delta); + else + then = ns_to_timespec64(delta); + phytmac_get_time(pdata, &now); + now = timespec64_add(now, then); + phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + } else { + adj = (neg << PHYTMAC_INCR_ADD_INDEX) | delta; + PHYTMAC_WRITE(pdata, PHYTMAC_TA, adj); + } + + return 0; +} + +static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +{ + int ts_valid = 0; + + if (direction == PHYTMAC_TX) + ts_valid = desc->desc1 & PHYTMAC_BIT(TX_TS_VALID); + else if (direction == PHYTMAC_RX) + ts_valid = desc->desc0 & PHYTMAC_BIT(RX_TS_VALID); + return ts_valid; +} + +static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +{ + struct timespec64 ts2; + + ts->tv_sec = (PHYTMAC_GET_BITS(ts_2, DMA_SECH) << 2) | + PHYTMAC_GET_BITS(ts_1, DMA_SECL); + ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, DMA_NSEC); + + phytmac_get_time(pdata, &ts2); + + if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_DMA_SEC_TOP >> 1)) != 0) + ts->tv_sec -= PHYTMAC_DMA_SEC_TOP; + + ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_DMA_SEC_MASK)); +} + +static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +{ + return 300000000; +} + +struct phytmac_hw_if phytmac_1p0_hw = { + .reset_hw = phytmac_reset_hw, + .init_hw = phytmac_init_hw, + .init_ring_hw = phytmac_init_ring_hw, + .get_feature = phytmac_get_feature_all, + .get_regs = phytmac_get_regs, + .get_stats = phytmac_get_hw_stats, + .set_mac_address = phytmac_set_mac_addr, + .get_mac_address = phytmac_get_mac_addr, + .mdio_read = phytmac_mdio_data_read_c22, + .mdio_write = phytmac_mdio_data_write_c22, + .mdio_read_c45 = phytmac_mdio_data_read_c45, + .mdio_write_c45 = phytmac_mdio_data_write_c45, + .poweron = phytmac_powerup_hw, + .set_wol = phytmac_set_wake, + .enable_promise = phytmac_enable_promise, + .enable_multicast = phytmac_enable_multicast, + .set_hash_table = phytmac_set_mc_hash, + .enable_rx_csum = phytmac_enable_rxcsum, + .enable_tx_csum = phytmac_enable_txcsum, + .enable_mdio_control = phytmac_enable_mdio, + .enable_autoneg = phytmac_enable_autoneg, + .enable_pause = phytmac_enable_pause, + .enable_network = phytmac_enable_network, + .add_fdir_entry = phytmac_add_fdir_entry, + .del_fdir_entry = phytmac_del_fdir_entry, + /* mac config */ + .mac_config = phytmac_mac_interface_config, + .mac_linkup = phytmac_mac_linkup, + .mac_linkdown = phytmac_mac_linkdown, + .pcs_linkup = phytmac_pcs_linkup, + .pcs_linkdown = phytmac_pcs_linkdown, + .get_link = phytmac_pcs_get_link, + /* irq */ + .enable_irq = phytmac_enable_irq, + .disable_irq = phytmac_disable_irq, + .clear_irq = phytmac_clear_irq, + .get_irq = phytmac_get_irq, + .get_intx_mask = phytmac_get_intx_mask, + /* tx and rx */ + .tx_map = phytmac_tx_map_desc, + .transmit = phytmac_tx_start, + .restart = phytmac_restart, + .tx_complete = phytmac_tx_complete, + .rx_complete = phytmac_rx_complete, + .get_rx_pkt_len = phytmac_rx_pkt_len, + .get_desc_addr = phytmac_get_desc_addr, + .init_rx_map = phytmac_init_rx_map_desc, + .rx_map = phytmac_rx_map_desc, + .rx_clean = phytmac_rx_clean_desc, + .rx_checksum = phytmac_rx_checksum, + .rx_single_buffer = phytmac_rx_single_buffer, + .rx_pkt_start = phytmac_rx_sof, + .rx_pkt_end = phytmac_rx_eof, + .clear_rx_desc = phytmac_clear_rx_desc, + .clear_tx_desc = phytmac_clear_tx_desc, + /* ptp */ + .init_ts_hw = phytmac_ptp_init_hw, + .set_time = phytmac_set_time, + .clear_time = phytmac_clear_time, + .get_time = phytmac_get_time, + .set_ts_config = phytmac_set_tsmode, + .set_incr = phytmac_set_tsincr, + .adjust_fine = phytmac_adjust_fine, + .adjust_time = phytmac_adjust_time, + .ts_valid = phytmac_ts_valid, + .get_timestamp = phytmac_get_dma_ts, + .get_ts_rate = phytmac_get_ts_rate, +}; +EXPORT_SYMBOL_GPL(phytmac_1p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h new file mode 100644 index 00000000000000..6f2b521aaebdbb --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -0,0 +1,453 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_V1_H +#define _PHYTMAC_V1_H + +extern struct phytmac_hw_if phytmac_1p0_hw; + +#define PHYTMAC_FRAME_MASK 0x1fff +#define PHYTMAC_JUMBO_FRAME_MASK 0x3fff + +#define PHYTMAC_SPEED_100M 0 +#define PHYTMAC_SPEED_1000M 1 +#define PHYTMAC_SPEED_2500M 2 +#define PHYTMAC_SPEED_5000M 3 +#define PHYTMAC_SPEED_10000M 4 +#define PHYTMAC_SERDES_RATE_5G 0 +#define PHYTMAC_SERDES_RATE_10G 1 + +#define PHYTMAC_NCTRL 0x0000 +#define PHYTMAC_NCONFIG 0x0004 +#define PHYTMAC_NSTATUS 0x0008 +#define PHYTMAC_DCONFIG 0x0010 +#define PHYTMAC_RXPTR_Q0 0x0018 +#define PHYTMAC_TXPTR_Q0 0x001C +#define PHYTMAC_IS 0x0024 +#define PHYTMAC_IE 0x0028 +#define PHYTMAC_ID 0x002C +#define PHYTMAC_IM 0x0030 +#define PHYTMAC_MDATA 0x0034 +#define PHYTMAC_HCONFIG 0x0050 +#define PHYTMAC_AXICTRL 0x0054 +#define PHYTMAC_INT_MODERATION 0x005C +#define PHYTMAC_HASHB 0x0080 +#define PHYTMAC_HASHT 0x0084 +#define PHYTMAC_MAC1B 0x0088 +#define PHYTMAC_MAC1T 0x008C +#define PHYTMAC_WOL 0x00B8 +#define PHYTMAC_OCTTX 0x0100 +#define PHYTMAC_TISN 0x01BC +#define PHYTMAC_TSH 0x01C0 +#define PHYTMAC_TSL 0x01D0 +#define PHYTMAC_TN 0x01D4 +#define PHYTMAC_TA 0x01D8 +#define PHYTMAC_TI 0x01DC +#define PHYTMAC_PCSCTRL 0x0200 +#define PHYTMAC_PCSSTATUS 0x0204 +#define PHYTMAC_PCSANLPBASE 0x0214 +#define PHYTMAC_DEFAULT1 0x0280 /* Default HW Config 1 */ +#define PHYTMAC_DEFAULT2 0x0294 /* Default HW Config 2 */ +#define PHYTMAC_DEFAULT3 0x029C /* Default HW Config 3 */ +#define PHYTMAC_DEFAULT4 0x02A4 /* Default HW Config 4 */ +#define PHYTMAC_DEFAULT5 0x02AC /* Default HW Config 5 */ +#define PHYTMAC_USXCTRL 0x0A80 +#define PHYTMAC_USXSTATUS 0x0A88 +#define PHYTMAC_TXBDCTRL 0x04CC +#define PHYTMAC_RXBDCTRL 0x04D0 + +/* Fdir match registers */ +#define PHYTMAC_FDIR(i) (0x0540 + ((i) << 2)) + +/* EtherType registers */ +#define PHYTMAC_ETHT(i) (0x06E0 + ((i) << 2)) + +/* Fdir compare registers */ +#define PHYTMAC_COMP1(i) (0x0700 + ((i) << 3)) +#define PHYTMAC_COMP2(i) (0x0704 + ((i) << 3)) + +#define PHYTMAC_ISR(i) (0x0400 + ((i) << 2)) +#define PHYTMAC_TXPTR(i) (0x0440 + ((i) << 2)) +#define PHYTMAC_RXPTR(i) (0x0480 + ((i) << 2)) +#define PHYTMAC_RBQS(i) (0x04A0 + ((i) << 2)) +#define PHYTMAC_TXPTRH(i) (0x04c8) +#define PHYTMAC_RXPTRH(i) (0x04d4) + +#define PHYTMAC_IER(i) (0x0600 + ((i) << 2)) +#define PHYTMAC_IDR(i) (0x0620 + ((i) << 2)) +#define PHYTMAC_IMR(i) (0x0640 + ((i) << 2)) +#define PHYTMAC_TAIL_ENABLE (0x0e7c) +#define PHYTMAC_TAILPTR(i) (0x0e80 + ((i) << 2)) + +#define PHYTMAC_PHY_INT_ENABLE 0x1C88 +#define PHYTMAC_PHY_INT_CLEAR 0x1C8C +#define PHYTMAC_PHY_INT_STATE 0x1C90 +#define PHYTMAC_INTX_IRQ_MASK 0x1C7C + +#define PHYTMAC_READ_NSTATUS(pdata) PHYTMAC_READ(pdata, PHYTMAC_NSTATUS) + +/* Ethernet Network Control Register */ +#define PHYTMAC_RE_INDEX 2 /* Receive enable */ +#define PHYTMAC_RE_WIDTH 1 +#define PHYTMAC_TE_INDEX 3 /* Transmit enable */ +#define PHYTMAC_TE_WIDTH 1 +#define PHYTMAC_MPE_INDEX 4 /* Management port enable */ +#define PHYTMAC_MPE_WIDTH 1 +#define PHYTMAC_CLEARSTAT_INDEX 5 /* Clear stats regs */ +#define PHYTMAC_CLEARSTAT_WIDTH 1 +#define PHYTMAC_TSTART_INDEX 9 /* Start transmission */ +#define PHYTMAC_TSTART_WIDTH 1 +#define PHYTMAC_THALT_INDEX 10 /* Transmit halt */ +#define PHYTMAC_THALT_WIDTH 1 +#define PHYTMAC_STORE_RX_TS_INDEX 15 +#define PHYTMAC_STORE_RX_TS_WIDTH 1 +#define PHYTMAC_OSSMODE_INDEX 24 /* Enable One Step Synchro Mode */ +#define PHYTMAC_OSSMODE_WIDTH 1 +#define PHYTMAC_2PT5G_INDEX 29 /* 2.5G operation selected */ +#define PHYTMAC_2PT5G_WIDTH 1 +#define PHYTMAC_HIGHSPEED_INDEX 31 /* High speed enable */ +#define PHYTMAC_HIGHSPEED_WIDTH 1 + +/* Ethernet Network Config Register */ +#define PHYTMAC_SPEED_INDEX 0 /* Speed */ +#define PHYTMAC_SPEED_WIDTH 1 +#define PHYTMAC_FD_INDEX 1 /* Full duplex */ +#define PHYTMAC_FD_WIDTH 1 +#define PHYTMAC_JUMBO_EN_INDEX 3 /* Transmit enable */ +#define PHYTMAC_JUMBO_EN_WIDTH 1 +#define PHYTMAC_PROMISC_INDEX 4 /* Copy all frames */ +#define PHYTMAC_PROMISC_WIDTH 1 +#define PHYTMAC_NO_BCAST_INDEX 5 /* No broadcast */ +#define PHYTMAC_NO_BCAST_WIDTH 1 +#define PHYTMAC_MH_EN_INDEX 6 /* Multicast hash enable */ +#define PHYTMAC_MH_EN_WIDTH 1 +#define PHYTMAC_RCV_BIG_INDEX 8 +#define PHYTMAC_RCV_BIG_WIDTH 1 +#define PHYTMAC_GM_EN_INDEX 10 /* Gigabit mode enable */ +#define PHYTMAC_GM_EN_WIDTH 1 +#define PHYTMAC_PCS_EN_INDEX 11 /* PCS select */ +#define PHYTMAC_PCS_EN_WIDTH 1 +#define PHYTMAC_PAUSE_EN_INDEX 13 /* Pause enable */ +#define PHYTMAC_PAUSE_EN_WIDTH 1 +#define PHYTMAC_FCS_REMOVE_INDEX 17 /* FCS remove */ +#define PHYTMAC_FCS_REMOVE_WIDTH 1 +#define PHYTMAC_MCD_INDEX 18 /* MDC clock division */ +#define PHYTMAC_MCD_WIDTH 3 +#define PHYTMAC_DBW64_INDEX 21 /* Data bus width */ +#define PHYTMAC_DBW64_WIDTH 1 +#define PHYTMAC_DBW128_INDEX 22 /* Data bus width */ +#define PHYTMAC_DBW128_WIDTH 1 +#define PHYTMAC_DBW_32 1 +#define PHYTMAC_DBW_64 2 +#define PHYTMAC_DBW_128 4 +#define PHYTMAC_RCO_EN_INDEX 24 /* Receive checksum offload enable */ +#define PHYTMAC_RCO_EN_WIDTH 1 +#define PHYTMAC_SGMII_EN_INDEX 27 /* Sgmii mode enable */ +#define PHYTMAC_SGMII_EN_WIDTH 1 + +/* Ethernet Network Status Register */ +#define PHYTMAC_PCS_LINK_INDEX 0 /* PCS link status */ +#define PHYTMAC_PCS_LINK_WIDTH 1 +#define PHYTMAC_MDIO_IDLE_INDEX 2 /* Mdio idle */ +#define PHYTMAC_MDIO_IDLE_WIDTH 1 +#define PHYTMAC_PCS_FD_INDEX 3 /* PCS auto negotiation duplex resolution */ +#define PHYTMAC_PCS_FD__WIDTH 1 + +/* Ethernet Network Dma config Register */ +#define PHYTMAC_BURST_INDEX 0 /* Amba burst length */ +#define PHYTMAC_BURST_WIDTH 5 +#define PHYTMAC_ENDIA_PKT_INDEX 6 +#define PHYTMAC_ENDIA_PKT_WIDTH 1 +#define PHYTMAC_ENDIA_DESC_INDEX 7 +#define PHYTMAC_ENDIA_DESC_WIDTH 1 +#define PHYTMAC_TCO_EN_INDEX 11 /* Tx Checksum Offload en */ +#define PHYTMAC_TCO_EN_WIDTH 1 +#define PHYTMAC_RX_BUF_LEN_INDEX 16 /* DMA receive buffer size */ +#define PHYTMAC_RX_BUF_LEN_WIDTH 8 +#define PHYTMAC_RX_EXBD_EN_INDEX 28 /* Enable RX extended BD mode */ +#define PHYTMAC_RX_EXBD_EN_WIDTH 1 +#define PHYTMAC_TX_EXBD_EN_INDEX 29 /* Enable TX extended BD mode */ +#define PHYTMAC_TX_EXBD_EN_WIDTH 1 +#define PHYTMAC_ABW_INDEX 30 /* DMA address bus width */ +#define PHYTMAC_ABW_WIDTH 1 + +/* Int stauts/Enable/Disable/Mask Register */ +#define PHYTMAC_RXCOMP_INDEX 1 /* Rx complete */ +#define PHYTMAC_RXCOMP_WIDTH 1 +#define PHYTMAC_RUB_INDEX 2 /* Rx used bit read */ +#define PHYTMAC_RUB_WIDTH 1 +#define PHYTMAC_BUS_ERR_INDEX 6 /* AMBA error */ +#define PHYTMAC_BUS_ERR_WIDTH 1 +#define PHYTMAC_TXCOMP_INDEX 7 /* Tx complete */ +#define PHYTMAC_TXCOMP_WIDTH 1 +#define PHYTMAC_RXOVERRUN_INDEX 10 /* Tx overrun */ +#define PHYTMAC_RXOVERRUN_WIDTH 1 +#define PHYTMAC_RESP_ERR_INDEX 11 /* Resp not ok */ +#define PHYTMAC_RESP_ERR_WIDTH 1 + +/* Mdio read/write Register */ +#define PHYTMAC_DATA_INDEX 0 /* Data */ +#define PHYTMAC_DATA_WIDTH 16 +#define PHYTMAC_MUST_INDEX 16 /* Must Be 10 */ +#define PHYTMAC_MUST_WIDTH 2 +#define PHYTMAC_REG_ADDR_INDEX 18 /* Register address */ +#define PHYTMAC_REG_ADDR_WIDTH 5 +#define PHYTMAC_PHY_ADDR_INDEX 23 /* Phy address */ +#define PHYTMAC_PHY_ADDR_WIDTH 5 +#define PHYTMAC_OPS_INDEX 28 +#define PHYTMAC_OPS_WIDTH 2 +#define PHYTMAC_CLAUSE_SEL_INDEX 30 +#define PHYTMAC_CLAUSE_SEL_WIDTH 1 +#define PHYTMAC_CLAUSE_C22 1 +#define PHYTMAC_CLAUSE_C45 0 +#define PHYTMAC_OPS_C45_ADDR 0 +#define PHYTMAC_OPS_C45_WRITE 1 +#define PHYTMAC_OPS_C45_READ 3 +#define PHYTMAC_OPS_C22_WRITE 1 +#define PHYTMAC_OPS_C22_READ 2 + +/* hs mac config register */ +#define PHYTMAC_HS_SPEED_INDEX 0 +#define PHYTMAC_HS_SPEED_WIDTH 3 +#define PHYTMAC_HS_SPEED_100M 0 +#define PHYTMAC_HS_SPEED_1000M 1 +#define PHYTMAC_HS_SPEED_2500M 2 +#define PHYTMAC_HS_SPEED_5000M 3 +#define PHYTMAC_HS_SPEED_10G 4 + +/* WOL register */ +#define PHYTMAC_ARP_IP_INDEX 0 +#define PHYTMAC_ARP_IP_WIDTH 16 +#define PHYTMAC_MAGIC_INDEX 16 +#define PHYTMAC_MAGIC_WIDTH 1 +#define PHYTMAC_ARP_INDEX 17 +#define PHYTMAC_ARP_WIDTH 1 +#define PHYTMAC_UCAST_INDEX 18 +#define PHYTMAC_UCAST_WIDTH 1 +#define PHYTMAC_MCAST_INDEX 19 +#define PHYTMAC_MCAST_WIDTH 1 + +/* PCSCTRL register */ +#define PHYTMAC_AUTONEG_INDEX 12 +#define PHYTMAC_AUTONEG_WIDTH 1 + +/* DEFAULT1 register */ +#define PHYTMAC_DBW_INDEX 25 +#define PHYTMAC_DBW_WIDTH 3 + +/* DEFAULT2 register */ +#define PHYTMAC_DAW64_INDEX 23 +#define PHYTMAC_DAW64_WIDTH 1 + +/* DEFAULT3 register */ +#define PHYTMAC_SCR2CMP_INDEX 0 +#define PHYTMAC_SCR2CMP_WIDTH 8 +#define PHYTMAC_SCR2ETH_INDEX 8 +#define PHYTMAC_SCR2ETH_WIDTH 8 + +/* DEFAULT4 register */ +#define PHYTMAC_TXDESCRD_INDEX 12 +#define PHYTMAC_TXDESCRD_WIDTH 4 +#define PHYTMAC_RXDESCRD_INDEX 8 +#define PHYTMAC_RXDESCRD_WIDTH 4 + +/* USXCTRL register */ +#define PHYTMAC_RX_EN_INDEX 0 +#define PHYTMAC_RX_EN_WIDTH 1 +#define PHYTMAC_TX_EN_INDEX 1 +#define PHYTMAC_TX_EN_WIDTH 1 +#define PHYTMAC_RX_SYNC_RESET_INDEX 2 +#define PHYTMAC_RX_SYNC_RESET_WIDTH 1 +#define PHYTMAC_SERDES_RATE_INDEX 12 +#define PHYTMAC_SERDES_RATE_WIDTH 2 +#define PHYTMAC_USX_SPEED_INDEX 14 +#define PHYTMAC_USX_SPEED_WIDTH 3 + +/* Bitfields in USX_STATUS. */ +#define PHYTMAC_USX_PCS_LINK_INDEX 0 +#define PHYTMAC_USX_PCS_LINK_WIDTH 1 + +/* Bitfields in PHYTMAC_TISN */ +#define PHYTMAC_SUB_NSECH_INDEX 0 +#define PHYTMAC_SUB_NSECH_WIDTH 16 +#define PHYTMAC_SUB_NSECL_INDEX 24 +#define PHYTMAC_SUB_NSECL_WIDTH 8 +#define PHYTMAC_SUB_NSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TSH */ +#define PHYTMAC_SECH_INDEX 0 +#define PHYTMAC_SECH_WIDTH 16 + +/* Bitfields in PHYTMAC_TSL */ +#define PHYTMAC_SECL_INDEX 0 +#define PHYTMAC_SECL_WIDTH 32 + +/* Bitfields in PHYTMAC_TN */ +#define PHYTMAC_NSEC_INDEX 0 +#define PHYTMAC_NSEC_WIDTH 30 + +/* Bitfields in PHYTMAC_TA */ +#define PHYTMAC_INCR_SEC_INDEX 0 +#define PHYTMAC_INCR_SEC_WIDTH 30 +#define PHYTMAC_INCR_ADD_INDEX 31 +#define PHYTMAC_INCR_ADD_WIDTH 1 +#define PHYTMAC_ADJUST_SEC_MAX ((1 << PHYTMAC_INCR_SEC_WIDTH) - 1) + +/* Bitfields in PHYTMAC_TI */ +#define PHYTMAC_INCR_NS_INDEX 0 +#define PHYTMAC_INCR_NS_WIDTH 8 + +/* PHYTMAC_TXBDCTRL register */ +#define PHYTMAC_TX_TSMODE_INDEX 4 +#define PHYTMAC_TX_TSMODE_WIDTH 2 + +#define PHYTMAC_RX_TSMODE_INDEX 4 +#define PHYTMAC_RX_TSMODE_WIDTH 2 + +/* Bitfields in PHYTMAC_FDIR */ +#define PHYTMAC_QUEUE_NUM_INDEX 0 +#define PHYTMAC_QUEUE_NUM_WIDTH 4 +#define PHYTMAC_VLAN_PRI_INDEX 4 +#define PHYTMAC_VLAN_PRI_WIDTH 3 +#define PHYTMAC_VLAN_EN_INDEX 8 +#define PHYTMAC_VLAN_EN_WIDTH 1 +#define PHYTMAC_ETH_TYPE_INDEX 9 +#define PHYTMAC_ETH_TYPE_WIDTH 3 +#define PHYTMAC_ETH_EN_INDEX 12 +#define PHYTMAC_ETH_EN_WIDTH 1 +#define PHYTMAC_CA_INDEX 13 +#define PHYTMAC_CA_WIDTH 5 +#define PHYTMAC_CA_EN_INDEX 18 +#define PHYTMAC_CA_EN_WIDTH 1 +#define PHYTMAC_CB_INDEX 19 +#define PHYTMAC_CB_WIDTH 5 +#define PHYTMAC_CB_EN_INDEX 24 +#define PHYTMAC_CB_EN_WIDTH 1 +#define PHYTMAC_CC_INDEX 25 +#define PHYTMAC_CC_WIDTH 5 +#define PHYTMAC_CC_EN_INDEX 30 +#define PHYTMAC_CC_EN_WIDTH 1 + +/* Bitfields in PHYTMAC_ETHERTYPE */ +#define PHYTMAC_ETHTYPE_VALUE_INDEX 0 +#define PHYTMAC_ETHTYPE_VALUE_WIDTH 16 + +/* Bitfields in PHYTMAC_COMP1 */ +#define PHYTMAC_SPORT_INDEX 0 +#define PHYTMAC_SPORT_WIDTH 16 +#define PHYTMAC_DPORT_INDEX 16 +#define PHYTMAC_DPORTE_WIDTH 16 + +/* Bitfields in PHYTMAC_COMP2 */ +#define PHYTMAC_OFFSET_INDEX 0 +#define PHYTMAC_OFFSET_WIDTH 7 +#define ETHTYPE_SIP_OFFSET 12 +#define ETHTYPE_DIP_OFFSET 16 +#define IPHEAD_SPORT_OFFSET 0 +#define IPHEAD_DPORT_OFFSET 2 +#define PHYTMAC_OFFSET_TYPE_INDEX 7 +#define PHYTMAC_OFFSET_TYPE_WIDTH 2 +#define PHYTMAC_OFFSET_BEGIN 0 +#define PHYTMAC_OFFSET_AFTER_L2HEAD 1 +#define PHYTMAC_OFFSET_AFTER_L3HEAD 2 +#define PHYTMAC_OFFSET_AFTER_L4HEAD 3 +#define PHYTMAC_DIS_MASK_INDEX 9 +#define PHYTMAC_DIS_MASK_WIDTH 1 +#define PHYTMAC_VLAN_ID_INDEX 10 +#define PHYTMAC_VLAN_ID_WIDTH 1 + +#define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) +#define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) +#define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) + +/* rx dma desc bit */ +/* DMA descriptor bitfields */ +#define PHYTMAC_RX_USED_INDEX 0 +#define PHYTMAC_RX_USED_WIDTH 1 +#define PHYTMAC_RX_WRAP_INDEX 1 +#define PHYTMAC_RX_WRAP_WIDTH 1 +#define PHYTMAC_RX_TS_VALID_INDEX 2 +#define PHYTMAC_RX_TS_VALID_WIDTH 1 +#define PHYTMAC_RX_WADDR_INDEX 2 +#define PHYTMAC_RX_WADDR_WIDTH 30 + +#define PHYTMAC_RX_FRMLEN_INDEX 0 +#define PHYTMAC_RX_FRMLEN_WIDTH 12 +#define PHYTMAC_RX_INDEX_INDEX 12 +#define PHYTMAC_RX_INDEX_WIDTH 2 +#define PHYTMAC_RX_SOF_INDEX 14 +#define PHYTMAC_RX_SOF_WIDTH 1 +#define PHYTMAC_RX_EOF_INDEX 15 +#define PHYTMAC_RX_EOF_WIDTH 1 +#define PHYTMAC_RX_CFI_INDEX 16 +#define PHYTMAC_RX_CFI_WIDTH 1 +#define PHYTMAC_RX_VLAN_PRI_INDEX 17 +#define PHYTMAC_RX_VLAN_PRI_WIDTH 3 +#define PHYTMAC_RX_PRI_TAG_INDEX 20 +#define PHYTMAC_RX_PRI_TAG_WIDTH 1 +#define PHYTMAC_RX_VLAN_TAG_INDEX 21 +#define PHYTMAC_RX_VLAN_TAG_WIDTH 1 +#define PHYTMAC_RX_UHASH_MATCH_INDEX 29 +#define PHYTMAC_RX_UHASH_MATCH_WIDTH 1 +#define PHYTMAC_RX_MHASH_MATCH_INDEX 30 +#define PHYTMAC_RX_MHASH_MATCH_WIDTH 1 +#define PHYTMAC_RX_BROADCAST_INDEX 31 +#define PHYTMAC_RX_BROADCAST_WIDTH 1 + +#define PHYTMAC_RX_FRMLEN_MASK 0x1FFF +#define PHYTMAC_RX_JFRMLEN_MASK 0x3FFF + +/* RX checksum offload disabled: bit 24 clear in NCFGR */ +#define PHYTMAC_RX_TYPEID_MATCH_INDEX 22 +#define PHYTMAC_RX_TYPEID_MATCH_WIDTH 2 + +/* RX checksum offload enabled: bit 24 set in NCFGR */ +#define PHYTMAC_RX_CSUM_INDEX 22 +#define PHYTMAC_RX_CSUM_WIDTH 2 + +/* tx dma desc bit */ +#define PHYTMAC_TX_FRMLEN_INDEX 0 +#define PHYTMAC_TX_FRMLEN_WIDTH 14 +#define PHYTMAC_TX_LAST_INDEX 15 +#define PHYTMAC_TX_LAST_WIDTH 1 +#define PHYTMAC_TX_NOCRC_INDEX 16 +#define PHYTMAC_TX_NOCRC_WIDTH 1 +#define PHYTMAC_MSS_MFS_INDEX 16 +#define PHYTMAC_MSS_MFS_WIDTH 14 +#define PHYTMAC_TX_LSO_INDEX 17 +#define PHYTMAC_TX_LSO_WIDTH 2 +#define PHYTMAC_TX_TCP_SEQ_SRC_INDEX 19 +#define PHYTMAC_TX_TCP_SEQ_SRC_WIDTH 1 +#define PHYTMAC_TX_TS_VALID_INDEX 23 +#define PHYTMAC_TX_TS_VALID_WIDTH 1 +#define PHYTMAC_TX_BUF_EXHAUSTED_INDEX 27 +#define PHYTMAC_TX_BUF_EXHAUSTED_WIDTH 1 +#define PHYTMAC_TX_UNDERRUN_INDEX 28 +#define PHYTMAC_TX_UNDERRUN_WIDTH 1 +#define PHYTMAC_TX_ERROR_INDEX 29 +#define PHYTMAC_TX_ERROR_WIDTH 1 +#define PHYTMAC_TX_WRAP_INDEX 30 +#define PHYTMAC_TX_WRAP_WIDTH 1 +#define PHYTMAC_TX_USED_INDEX 31 +#define PHYTMAC_TX_USED_WIDTH 1 + +/* Transmit DMA buffer descriptor Word 4 */ +#define PHYTMAC_DMA_NSEC_INDEX 0 +#define PHYTMAC_DMA_NSEC_WIDTH 30 +#define PHYTMAC_DMA_SECL_INDEX 30 +#define PHYTMAC_DMA_SECL_WIDTH 2 + +/* Transmit DMA buffer descriptor Word 4 */ +#define PHYTMAC_DMA_SECH_INDEX 0 +#define PHYTMAC_DMA_SECH_WIDTH 4 +#define PHYTMAC_DMA_SEC_MASK 0x3f +#define PHYTMAC_DMA_SEC_TOP 0x40 + +/* Buffer descriptor constants */ +#define PHYTMAC_RX_CSUM_NONE 0 +#define PHYTMAC_RX_CSUM_IP_ONLY 1 +#define PHYTMAC_RX_CSUM_IP_TCP 2 +#define PHYTMAC_RX_CSUM_IP_UDP 3 + +/* limit RX checksum offload to TCP and UDP packets */ +#define PHYTMAC_RX_CSUM_CHECKED_MASK 2 + +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c new file mode 100644 index 00000000000000..df142aa6797f68 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v2.h" + +static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, + u16 cmd_subid, void *data, int len, int wait) +{ + int index = 0; + struct phytmac_msg_info msg; + struct phytmac_msg_info msg_rx; + int ret = 0; + + ++pdata->msg_ring.tx_msg_tail; + if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) + pdata->msg_ring.tx_msg_tail = 1; + index = pdata->msg_ring.tx_msg_tail; + + wait = 1; + memset(&msg, 0, sizeof(msg)); + memset(&msg_rx, 0, sizeof(msg_rx)); + msg.module_id = PHYTMAC_MODULE_ID_GMAC; + msg.cmd_id = cmd_id; + msg.cmd_subid = cmd_subid; + msg.flags = PHYTMAC_FLAGS_MSG_NOINT; + + if (len) + memcpy(&msg.para[0], data, len); + + if (netif_msg_hw(pdata)) { + netdev_info(pdata->ndev, "tx msg: cmdid=%d, subid=%d, flags=%d, len=%d, tail=%d\n", + msg.cmd_id, msg.cmd_subid, msg.flags, len, pdata->msg_ring.tx_msg_tail); + } + + memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, + pdata->msg_ring.tx_msg_tail | PHYTMAC_BIT(TX_MSG_INT)); + + if (wait) { + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + while (!(msg_rx.flags & 0x1)) { + cpu_relax(); + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + } + } + + return ret; +} + +void phytmac_reset_hw(struct phytmac *pdata) +{ + int q; + u16 cmd_id, cmd_subid; + struct phytmac_ring_info ring; + + /* Disable and clear all interrupts and disable queues */ + for (q = 0; q < pdata->queues_max_num; ++q) { + PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(q), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(q), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), 0); + } + + /* reset hw rx/tx enable */ + cmd_id = PHYTMAC_MSG_CMD_DEFAULT; + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_HW; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + /* reset tx ring */ + memset(&ring, 0, sizeof(ring)); + ring.queue_num = pdata->queues_max_num; + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); + + /* reset rx ring */ + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); +} + +static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +{ + int index; + u16 cmd_id, cmd_subid; + struct phytmac_mac para; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + sizeof(struct phytmac_mac)); + + addr[0] = para.addrl & 0xff; + addr[1] = (para.addrl >> 8) & 0xff; + addr[2] = (para.addrl >> 16) & 0xff; + addr[3] = (para.addrl >> 24) & 0xff; + addr[4] = para.addrh & 0xff; + addr[5] = (para.addrh >> 8) & 0xff; + + return 0; +} + +int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +{ + u16 cmd_id; + u16 cmd_subid; + struct phytmac_mac para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ADDR; + para.addrl = cpu_to_le32(*((u32 *)addr)); + para.addrh = cpu_to_le16(*((u16 *)(addr + 4))); + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_init_hw(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + struct phytmac_dma_info dma; + struct phytmac_eth_info eth; + + phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + if (pdata->ndev->flags & IFF_PROMISC) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + if (pdata->ndev->features & NETIF_F_RXCSUM) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + if (!(pdata->ndev->flags & IFF_BROADCAST)) { + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_BC; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + memset(&dma, 0, sizeof(dma)); + cmd_subid = PHYTMAC_MSG_CMD_SET_DMA; + dma.dma_burst_length = pdata->dma_burst_length; + if (pdata->dma_addr_width) + dma.hw_dma_cap |= HW_DMA_CAP_64B; + if (pdata->ndev->features & NETIF_F_HW_CSUM) + dma.hw_dma_cap |= HW_DMA_CAP_CSUM; + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + dma.hw_dma_cap |= HW_DMA_CAP_PTP; + if (pdata->dma_data_width == PHYTMAC_DBW64) + dma.hw_dma_cap |= HW_DMA_CAP_DDW64; + if (pdata->dma_data_width == PHYTMAC_DBW128) + dma.hw_dma_cap |= HW_DMA_CAP_DDW128; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + + memset(ð, 0, sizeof(eth)); + cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; + eth.index = 0; + eth.etype = (uint16_t)ETH_P_IP; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + + return 0; +} + +static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MC; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MC; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + return 0; +} + +static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +{ + u16 cmd_id, cmd_subid; + struct phytmac_mc_info para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC; + para.mc_bottom = (u32)mc_filter[0]; + para.mc_top = (u32)mc_filter[1]; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_init_ring_hw(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + struct phytmac_ring_info rxring; + struct phytmac_ring_info txring; + struct phytmac_rxbuf_info rxbuf; + struct phytmac_queue *queue; + u32 q; + + memset(&rxring, 0, sizeof(rxring)); + memset(&txring, 0, sizeof(txring)); + memset(&rxbuf, 0, sizeof(rxbuf)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_TX_RING; + txring.queue_num = pdata->queues_num; + rxring.queue_num = pdata->queues_num; + txring.hw_dma_cap |= HW_DMA_CAP_64B; + rxring.hw_dma_cap |= HW_DMA_CAP_64B; + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), queue->tx_head); + txring.addr[q] = queue->tx_ring_addr; + rxring.addr[q] = queue->rx_ring_addr; + } + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE; + rxbuf.queue_num = pdata->queues_num; + rxbuf.buffer_size = pdata->rx_buffer_len / 64; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + + return 0; +} + +int phytmac_init_msg_ring(struct phytmac *pdata) +{ + u32 size = 0; + + pdata->msg_ring.tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; + size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); + pdata->msg_ring.tx_msg_ring_size = size; + if (pdata->msg_ring.tx_msg_tail == size) + pdata->msg_ring.tx_msg_tail = 0; + + PHYTMAC_WRITE(pdata, PHYTMAC_MSG_IMR, 0xfffffffe); + if (netif_msg_hw(pdata)) + netdev_info(pdata->ndev, "mac msg ring: tx_msg_ring_size=%d, tx_msg_tail=%d\n", + size, pdata->msg_ring.tx_msg_tail); + + return 0; +} + +static int phytmac_get_feature_all(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + int index; + struct phytmac_feature para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + sizeof(struct phytmac_feature)); + + pdata->queues_max_num = para.queue_num; + if (para.dma_addr_width) + pdata->dma_addr_width = 64; + else + pdata->dma_addr_width = 32; + pdata->dma_data_width = para.dma_data_width; + pdata->max_rx_fs = para.max_rx_fs; + pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + + if (netif_msg_hw(pdata)) { + netdev_info(pdata->ndev, "feature qnum=%d, daw=%d, dbw=%d, rxfs=%d, rxbd=%d, txbd=%d\n", + pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, + pdata->max_rx_fs, pdata->rx_bd_prefetch, pdata->tx_bd_prefetch); + } + + return 0; +} + +void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +{ + u16 cmd_id, cmd_subid; + u16 reg_num; + int index; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_REG_READ; + reg_num = 16; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)®_num, 2, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); +} + +static void phytmac_get_hw_stats(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + u8 count; + int i, j, index; + u32 stats[48]; + u64 val; + u64 *p = &pdata->stats.tx_octets; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 1; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 2; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 3; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); + + for (i = 0; i < 3; i++) { + index = pdata->msg_ring.tx_msg_tail + i - 2; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + memcpy(&stats[i * 16], pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + } + + for (i = 0, j = 0; i < 44; i++) { + if (i == 0 || i == 20) { + val = (u64)stats[i + 1] << 32 | stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } else { + if (i != 1 && i != 21) { + val = stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } + } + } +} + +static void phytmac_mdio_idle(struct phytmac *pdata) +{ + u32 val; + + /* wait for end of transfer */ + val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); + while (!(val & PHYTMAC_BIT(MIDLE))) { + cpu_relax(); + val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); + } +} + +static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_READ) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, regnum) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; + phytmac_mdio_idle(pdata); + return data; +} + +static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_WRITE) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, regnum) + | PHYTMAC_BITS(VALUE, data) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, devad & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_READ) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, devad & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; + phytmac_mdio_idle(pdata); + return data; +} + +static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_WRITE) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, data) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_powerup_hw(struct phytmac *pdata, int on) +{ + u32 status, data0, data1, rdata1; + int ret; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh status is busy"); + + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy"); + + data0 = 0; + data0 = PHYTMAC_SET_BITS(data0, DATA0_MSG, PHYTMAC_MSG_PM); + data0 = PHYTMAC_SET_BITS(data0, DATA0_PRO, PHYTMAC_PRO_ID); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA0, data0); + data1 = 0; + + if (on == PHYTMAC_POWERON) { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATON); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } else { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATOFF); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } + + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_AP_CPP_SET, 1); + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy\n"); + + rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); + if (rdata1 == data1) + netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + else + netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + } + + pdata->power_state = on; + + return 0; +} + +static int phytmac_set_wake(struct phytmac *pdata, int wake) +{ + u16 cmd_id, cmd_subid; + u8 wol = (u8)wake; + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_WOL; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&wol), 1, 1); + + return 0; +} + +static int phytmac_enable_promise(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + u8 rxcsum = 0; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; + } else { + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PROMISE; + if (pdata->ndev->features & NETIF_F_RXCSUM) + rxcsum = 1; + } + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); + + return 0; +} + +static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_TXCSUM; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MDIO; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_AUTONEG; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + pdata->autoneg = enable; + return 0; +} + +static int phytmac_enable_pause(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_NETWORK; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; + struct phytmac_fdir_info fdir; + u16 cmd_id, cmd_subid; + + memset(&fdir, 0, sizeof(fdir)); + + tp4sp_v = &rx_flow->h_u.tcp_ip4_spec; + tp4sp_m = &rx_flow->m_u.tcp_ip4_spec; + if (tp4sp_m->ip4src == 0xFFFFFFFF) { + fdir.ipsrc_en = true; + fdir.ip4src = tp4sp_v->ip4src; + } + + if (tp4sp_m->ip4dst == 0xFFFFFFFF) { + fdir.ipdst_en = true; + fdir.ip4dst = tp4sp_v->ip4dst; + } + + if (tp4sp_m->psrc == 0xFFFF || tp4sp_m->pdst == 0xFFFF) { + fdir.port_en = true; + fdir.dstport = tp4sp_v->pdst; + fdir.srcport = tp4sp_v->psrc; + fdir.dstport_mask = tp4sp_m->pdst; + fdir.srcport_mask = tp4sp_m->psrc; + } + + fdir.location = rx_flow->location; + fdir.queue = rx_flow->ring_cookie; + + if (fdir.ipsrc_en || fdir.ipdst_en || fdir.port_en) { + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ADD_FDIR; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + } + + return 0; +} + +static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct phytmac_fdir_info fdir; + u16 cmd_id, cmd_subid; + + memset(&fdir, 0, sizeof(fdir)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_DEL_FDIR; + fdir.location = (u8)rx_flow->location; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + + return 0; +} + +static void phytmac_tx_start(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + queue->tx_xmit_more = 0; +} + +static u32 phytmac_get_irq_mask(u32 mask) +{ + u32 value = 0; + + value |= (mask & PHYTMAC_INT_TX_COMPLETE) ? PHYTMAC_BIT(TXCOMP) : 0; + value |= (mask & PHYTMAC_INT_TX_ERR) ? PHYTMAC_BIT(DMA_ERR) : 0; + value |= (mask & PHYTMAC_INT_RX_COMPLETE) ? PHYTMAC_BIT(RXCOMP) : 0; + value |= (mask & PHYTMAC_INT_RX_OVERRUN) ? PHYTMAC_BIT(RXOVERRUN) : 0; + value |= (mask & PHYTMAC_INT_RX_DESC_FULL) ? PHYTMAC_BIT(RUSED) : 0; + + return value; +} + +static u32 phytmac_get_irq_status(u32 value) +{ + u32 status = 0; + + status |= (value & PHYTMAC_BIT(TXCOMP)) ? PHYTMAC_INT_TX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(DMA_ERR)) ? PHYTMAC_INT_TX_ERR : 0; + status |= (value & PHYTMAC_BIT(RXCOMP)) ? PHYTMAC_INT_RX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(RXOVERRUN)) ? PHYTMAC_INT_RX_OVERRUN : 0; + status |= (value & PHYTMAC_BIT(RUSED)) ? PHYTMAC_INT_RX_DESC_FULL : 0; + + return status; +} + +static void phytmac_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_ER(queue_index), value); +} + +static void phytmac_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(queue_index), value); +} + +static void phytmac_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(queue_index), value); +} + +static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +{ + u32 status; + u32 value; + + value = PHYTMAC_READ(pdata, PHYTMAC_INT_SR(queue_index)); + status = phytmac_get_irq_status(value); + + return status; +} + +static void phytmac_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_CONFIG; + para.interface = state->interface; + para.autoneg = (mode == MLO_AN_FIXED ? 0 : 1); + para.speed = state->speed; + para.duplex = state->duplex; + pdata->autoneg = para.autoneg; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); +} + +static int phytmac_interface_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG; + para.interface = interface; + para.duplex = duplex; + para.speed = speed; + para.autoneg = pdata->autoneg; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_interface_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) { + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; + para.interface = interface; + para.duplex = duplex; + para.speed = speed; + para.autoneg = 0; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + } + + return 0; +} + +static int phytmac_pcs_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_2500BASEX) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_NETWORK_STATUS, LINK); + else if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_USX_LINK_STATUS, USX_LINK); + + return 0; +} + +static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) +{ + unsigned int i, ctrl; + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + struct phytmac_tx_skb *tx_skb; + unsigned int eof = 1; + + i = tx_tail; + + do { + i--; + tx_skb = phytmac_get_tx_skb(queue, i); + desc = phytmac_get_tx_desc(queue, i); + + ctrl = (u32)tx_skb->length; + if (eof) { + ctrl |= PHYTMAC_BIT(TXLAST); + eof = 0; + } + + if (unlikely(i == (pdata->tx_ring_size - 1))) + ctrl |= PHYTMAC_BIT(TXWRAP); + + if (i == queue->tx_tail) { + ctrl |= PHYTMAC_BITS(TXLSO, packet->lso); + ctrl |= PHYTMAC_BITS(TXTCP_SEQ_SRC, packet->seq); + if (packet->nocrc) + ctrl |= PHYTMAC_BIT(TXNOCRC); + } else { + ctrl |= PHYTMAC_BITS(MSSMFS, packet->mss); + } + + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* make newly desc1 to hardware */ + wmb(); + desc->desc1 = ctrl; + } while (i != queue->tx_tail); + + return 0; +} + +static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) +{ + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 |= PHYTMAC_BIT(RXUSED); +} + +static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + if (addr) { + if (unlikely(index == (pdata->rx_ring_size - 1))) + addr |= PHYTMAC_BIT(RXWRAP); + desc->desc1 = 0; + desc->desc2 = upper_32_bits(addr); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 &= ~PHYTMAC_BIT(RXUSED); + } + + return 0; +} + +static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc1, TXUSED); +} + +static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc0, RXUSED); +} + +static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +{ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + return desc->desc1 & PHYTMAC_RXJFRMLEN_MASK; + else + return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; +} + +static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) +{ + dma_addr_t addr = 0; + + addr = ((u64)(desc->desc2) << 32); + + addr |= (desc->desc0 & 0xfffffffc); + return addr; +} + +static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + u32 check = value >> PHYTMAC_RXCSUM_INDEX & 0x3; + + return (check == PHYTMAC_RXCSUM_IP_TCP || check == PHYTMAC_RXCSUM_IP_UDP); +} + +static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return ((value & PHYTMAC_BIT(RXSOF)) && (value & PHYTMAC_BIT(RXEOF))); +} + +static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RXSOF)); +} + +static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RXEOF)); +} + +static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +{ + unsigned int frag; + unsigned int tmp = end; + struct phytmac_dma_desc *desc; + + if (begin > end) + tmp = end + queue->pdata->rx_ring_size; + + for (frag = begin; frag != end; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + desc->desc0 &= ~PHYTMAC_BIT(RXUSED); + } +} + +static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc = NULL; + struct phytmac_tx_skb *tx_skb = NULL; + int i; + + for (i = 0; i < queue->pdata->tx_ring_size; i++) { + desc = phytmac_get_tx_desc(queue, i); + tx_skb = phytmac_get_tx_skb(queue, i); + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* make newly desc to hardware */ + wmb(); + desc->desc1 = PHYTMAC_BIT(TXUSED); + } + desc->desc1 |= PHYTMAC_BIT(TXWRAP); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); +} + +static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +{ + u32 ns, secl, sech; + + ns = PHYTMAC_READ(pdata, PHYTMAC_TIMER_NSEC); + secl = PHYTMAC_READ(pdata, PHYTMAC_TIMER_SEC); + sech = PHYTMAC_READ(pdata, PHYTMAC_TIMER_MSB_SEC); + + ts->tv_nsec = ns; + ts->tv_sec = (((u64)sech << 32) | secl) & TIMER_SEC_MAX_VAL; +} + +void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +{ + u32 secl, sech; + + secl = (u32)sec; + sech = (sec >> 32) & (0xffff); + + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_MSB_SEC, sech); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_SEC, secl); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, nsec); +} + +void phytmac_clear_time(struct phytmac *pdata) +{ + u32 value; + + pdata->ts_incr.sub_ns = 0; + pdata->ts_incr.ns = 0; + + value = PHYTMAC_READ(pdata, PHYTMAC_TIMER_INCR); + value = PHYTMAC_SET_BITS(value, INCR_NSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR, value); + + value = PHYTMAC_READ(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC); + value = PHYTMAC_SET_BITS(value, INCR_SNSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC, value); + + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, 0); +} + +int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +{ + u16 cmd_id, cmd_subid; + struct phytmac_ts_config para; + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_TS_CONFIG; + para.tx_mode = ctrl->tx_control; + para.rx_mode = ctrl->rx_control; + para.one_step = ctrl->one_step; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +{ + u32 value; + + value = PHYTMAC_BITS(INCR_SNSEC, incr->sub_ns); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC, value); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR, incr->ns); + + return 0; +} + +static void phytmac_ptp_init_hw(struct phytmac *pdata) +{ + struct timespec64 ts; + + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); + phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + + phytmac_set_tsincr(pdata, &pdata->ts_incr); +} + +static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +{ + struct ts_incr ts_incr; + u32 tmp; + u64 adj; + + ts_incr.ns = pdata->ts_incr.ns; + ts_incr.sub_ns = pdata->ts_incr.sub_ns; + + /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */ + tmp = ((u64)ts_incr.ns << PHYTMAC_INCR_SNSECL_INDEX) + ts_incr.sub_ns; + adj = ((u64)ppm * tmp + (USEC_PER_SEC >> 1)) >> PHYTMAC_INCR_SNSECL_INDEX; + + adj = div_u64(adj, USEC_PER_SEC); + adj = negative ? (tmp - adj) : (tmp + adj); + + ts_incr.ns = (adj >> PHYTMAC_INCR_SNSEC_WIDTH) + & ((1 << PHYTMAC_INCR_NSEC_WIDTH) - 1); + ts_incr.sub_ns = adj & ((1 << PHYTMAC_INCR_SNSEC_WIDTH) - 1); + + phytmac_set_tsincr(pdata, &ts_incr); + + return 0; +} + +int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +{ + u32 adj; + + if (delta > PHYTMAC_ASEC_MAX) { + struct timespec64 now, then; + + then = ns_to_timespec64(delta); + phytmac_get_time(pdata, &now); + now = timespec64_add(now, then); + phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + } else { + adj = (neg << PHYTMAC_AADD_INDEX) | delta; + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, adj); + } + + return 0; +} + +static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +{ + int ts_valid = 0; + + if (direction == PHYTMAC_TX) + ts_valid = desc->desc1 & PHYTMAC_BIT(TXTSVALID); + else if (direction == PHYTMAC_RX) + ts_valid = desc->desc0 & PHYTMAC_BIT(RXTSVALID); + return ts_valid; +} + +static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +{ + struct timespec64 ts2; + + ts->tv_sec = (PHYTMAC_GET_BITS(ts_2, TS_SECH) << 2) | + PHYTMAC_GET_BITS(ts_1, TS_SECL); + ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, TS_NSEC); + + phytmac_get_time(pdata, &ts2); + + if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_TS_SEC_TOP >> 1)) != 0) + ts->tv_sec -= PHYTMAC_TS_SEC_TOP; + + ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_TS_SEC_MASK)); +} + +static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +{ + return 300000000; +} + +struct phytmac_hw_if phytmac_2p0_hw = { + .init_msg_ring = phytmac_init_msg_ring, + .reset_hw = phytmac_reset_hw, + .init_hw = phytmac_init_hw, + .init_ring_hw = phytmac_init_ring_hw, + .get_feature = phytmac_get_feature_all, + .get_regs = phytmac_get_regs, + .get_stats = phytmac_get_hw_stats, + .set_mac_address = phytmac_set_mac_addr, + .get_mac_address = phytmac_get_mac_addr, + .mdio_read = phytmac_mdio_data_read_c22, + .mdio_write = phytmac_mdio_data_write_c22, + .mdio_read_c45 = phytmac_mdio_data_read_c45, + .mdio_write_c45 = phytmac_mdio_data_write_c45, + .poweron = phytmac_powerup_hw, + .set_wol = phytmac_set_wake, + .enable_promise = phytmac_enable_promise, + .enable_multicast = phytmac_enable_multicast, + .set_hash_table = phytmac_set_mc_hash, + .enable_rx_csum = phytmac_enable_rxcsum, + .enable_tx_csum = phytmac_enable_txcsum, + .enable_mdio_control = phytmac_enable_mdio, + .enable_autoneg = phytmac_enable_autoneg, + .enable_pause = phytmac_enable_pause, + .enable_network = phytmac_enable_network, + .add_fdir_entry = phytmac_add_fdir_entry, + .del_fdir_entry = phytmac_del_fdir_entry, + + /* mac config */ + .mac_config = phytmac_interface_config, + .mac_linkup = phytmac_interface_linkup, + .mac_linkdown = phytmac_interface_linkdown, + .pcs_linkup = phytmac_pcs_linkup, + .pcs_linkdown = phytmac_pcs_linkdown, + .get_link = phytmac_pcs_get_link, + + /* irq */ + .enable_irq = phytmac_enable_irq, + .disable_irq = phytmac_disable_irq, + .clear_irq = phytmac_clear_irq, + .get_irq = phytmac_get_irq, + + /* tx and rx */ + .tx_map = phytmac_tx_map_desc, + .transmit = phytmac_tx_start, + .tx_complete = phytmac_tx_complete, + .rx_complete = phytmac_rx_complete, + .get_rx_pkt_len = phytmac_rx_pkt_len, + .get_desc_addr = phytmac_get_desc_addr, + .init_rx_map = phytmac_init_rx_map_desc, + .rx_map = phytmac_rx_map_desc, + .rx_checksum = phytmac_rx_checksum, + .rx_single_buffer = phytmac_rx_single_buffer, + .rx_pkt_start = phytmac_rx_sof, + .rx_pkt_end = phytmac_rx_eof, + .clear_rx_desc = phytmac_clear_rx_desc, + .clear_tx_desc = phytmac_clear_tx_desc, + + /* ptp */ + .init_ts_hw = phytmac_ptp_init_hw, + .set_time = phytmac_set_time, + .clear_time = phytmac_clear_time, + .get_time = phytmac_get_time, + .set_ts_config = phytmac_set_tsmode, + .set_incr = phytmac_set_tsincr, + .adjust_fine = phytmac_adjust_fine, + .adjust_time = phytmac_adjust_time, + .ts_valid = phytmac_ts_valid, + .get_timestamp = phytmac_get_dma_ts, + .get_ts_rate = phytmac_get_ts_rate, +}; +EXPORT_SYMBOL_GPL(phytmac_2p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h new file mode 100644 index 00000000000000..92e4806ac2c1f7 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_V2_H +#define _PHYTMAC_V2_H + +extern struct phytmac_hw_if phytmac_2p0_hw; + +#define PHYTMAC_MSG_SRAM_SIZE 4096 +#define MSG_HDR_LEN 8 + +#define PHYTMAC_TX_MSG_HEAD 0x000 +#define PHYTMAC_TX_MSG_TAIL 0x004 +#define PHYTMAC_RX_MSG_HEAD 0x008 +#define PHYTMAC_RX_MSG_TAIL 0x00c +#define PHYTMAC_MSG_IMR 0x020 +#define PHYTMAC_MSG_ISR 0x02c + +#define PHYTMAC_SIZE 0x0048 +#define PHYTMAC_NETWORK_STATUS 0x0240 +#define PHYTMAC_PCS_AN_LP 0x0244 +#define PHYTMAC_USX_LINK_STATUS 0x0248 +#define PHYTMAC_MDIO 0x0264 +#define PHYTMAC_TIMER_INCR_SUB_NSEC 0x024c +#define PHYTMAC_TIMER_INCR 0x0250 +#define PHYTMAC_TIMER_MSB_SEC 0x0254 +#define PHYTMAC_TIMER_SEC 0x0258 +#define PHYTMAC_TIMER_NSEC 0x025c +#define PHYTMAC_TIMER_ADJUST 0x0260 +#define PHYTMAC_MSG(i) (((i) - 1) * 0x48) + +#define PHYTMAC_MODULE_ID_GMAC 0x60 +#define PHYTMAC_FLAGS_MSG_COMP 0x1 +#define PHYTMAC_FLAGS_MSG_NOINT 0x2 + +/* Bitfields in PHYTMAC_TX_MSG_TAIL */ +#define PHYTMAC_TX_MSG_INT_INDEX 16 +#define PHYTMAC_TX_MSG_INT_WIDTH 1 + +/* Bitfields in PHYTMAC_MSG_ISR */ +#define PHYTMAC_MSG_COMPLETE_INDEX 0 +#define PHYTMAC_MSG_COMPLETE_WIDTH 1 + +/* Bitfields in PHYTMAC_SIZE */ +#define PHYTMAC_MEM_SIZE_INDEX 0 +#define PHYTMAC_MEM_SIZE_WIDTH 4 +#define PHYTMAC_TXRING_SIZE_INDEX 8 +#define PHYTMAC_TXRING_SIZE_WIDTH 6 + +/* Bitfields in PHYTMAC_TIMER_INCR_SUB_NSEC */ +#define PHYTMAC_INCR_SNSECH_INDEX 0 +#define PHYTMAC_INCR_SNSECH_WIDTH 16 +#define PHYTMAC_INCR_SNSECL_INDEX 24 +#define PHYTMAC_INCR_SNSECL_WIDTH 8 +#define PHYTMAC_INCR_SNSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TIMER_INCR_SUB_NSEC */ +#define PHYTMAC_INCR_SNSEC_INDEX 0 +#define PHYTMAC_INCR_SNSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TIMER_INCR */ +#define PHYTMAC_INCR_NSEC_INDEX 0 +#define PHYTMAC_INCR_NSEC_WIDTH 8 + +/* Bitfields in PHYTMAC_TIMER_MSB_SEC */ +#define PHYTMAC_TIMER_SECH_INDEX 0 +#define PHYTMAC_TIMER_SECH_WIDTH 16 + +/* Bitfields in PHYTMAC_TIMER_SEC */ +#define PHYTMAC_TIMER_SECL_INDEX 0 +#define PHYTMAC_TIMER_SECL_WIDTH 32 + +/* Bitfields in PHYTMAC_TIMER_NSEC */ +#define PHYTMAC_TIMER_NSEC_INDEX 0 +#define PHYTMAC_TIMER_NSEC_WIDTH 30 + +/* Bitfields in PHYTMAC_TIMER_ADJUST */ +#define PHYTMAC_ASEC_INDEX 0 +#define PHYTMAC_ASEC_WIDTH 30 +#define PHYTMAC_AADD_INDEX 31 +#define PHYTMAC_AADD_WIDTH 1 +#define PHYTMAC_ASEC_MAX ((1 << PHYTMAC_ASEC_WIDTH) - 1) + +#define PHYTMAC_TIMER_SEC_WIDTH (PHYTMAC_TIMER_SECH_WIDTH + PHYTMAC_TIMER_SECL_WIDTH) +#define TIMER_SEC_MAX_VAL (((u64)1 << PHYTMAC_TIMER_SEC_WIDTH) - 1) +#define TIMER_NSEC_MAX_VAL ((1 << PHYTMAC_TIMER_NSEC_WIDTH) - 1) + +#define PHYTMAC_TAIL_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_INT_ER(i) (0x0140 + ((i) * 4)) +#define PHYTMAC_INT_DR(i) (0x0180 + ((i) * 4)) +#define PHYTMAC_INT_MR(i) (0x01c0 + ((i) * 4)) +#define PHYTMAC_INT_SR(i) (0x0200 + ((i) * 4)) + +#define PHYTMAC_LINK_INDEX 0 /* PCS link status */ +#define PHYTMAC_LINK_WIDTH 1 +#define PHYTMAC_MIDLE_INDEX 2 /* Mdio idle */ +#define PHYTMAC_MIDLE_WIDTH 1 + +/* Int stauts/Enable/Disable/Mask Register */ +#define PHYTMAC_RXCOMP_INDEX 1 /* Rx complete */ +#define PHYTMAC_RXCOMP_WIDTH 1 +#define PHYTMAC_RUSED_INDEX 2 /* Rx used bit read */ +#define PHYTMAC_RUSED_WIDTH 1 +#define PHYTMAC_DMA_ERR_INDEX 6 /* AMBA error */ +#define PHYTMAC_DMA_ERR_WIDTH 1 +#define PHYTMAC_TXCOMP_INDEX 7 /* Tx complete */ +#define PHYTMAC_TXCOMP_WIDTH 1 +#define PHYTMAC_RXOVERRUN_INDEX 10 /* Rx overrun */ +#define PHYTMAC_RXOVERRUN_WIDTH 1 +#define PHYTMAC_RESP_ERR_INDEX 11 /* Resp not ok */ +#define PHYTMAC_RESP_ERR_WIDTH 1 + +/* pcs an lp */ +#define PHYTMAC_AUTO_NEG_INDEX 12 +#define PHYTMAC_AUTO_NEG_WIDTH 1 + +/* Bitfields in USX_STATUS. */ +#define PHYTMAC_USX_LINK_INDEX 0 +#define PHYTMAC_USX_LINK_WIDTH 1 + +/* Mdio read/write Register */ +#define PHYTMAC_VALUE_INDEX 0 /* value */ +#define PHYTMAC_VALUE_WIDTH 16 +#define PHYTMAC_CONST_INDEX 16 /* Must Be 10 */ +#define PHYTMAC_CONST_WIDTH 2 +#define PHYTMAC_REGADDR_INDEX 18 /* Register address */ +#define PHYTMAC_REGADDR_WIDTH 5 +#define PHYTMAC_PHYADDR_INDEX 23 /* Phy address */ +#define PHYTMAC_PHYADDR_WIDTH 5 +#define PHYTMAC_MDCOPS_INDEX 28 +#define PHYTMAC_MDCOPS_WIDTH 2 +#define PHYTMAC_CLAUSESEL_INDEX 30 +#define PHYTMAC_CLAUSESEL_WIDTH 1 +#define PHYTMAC_C22 1 +#define PHYTMAC_C45 0 +#define PHYTMAC_C45_ADDR 0 +#define PHYTMAC_C45_WRITE 1 +#define PHYTMAC_C45_READ 3 +#define PHYTMAC_C22_WRITE 1 +#define PHYTMAC_C22_READ 2 + +/* rx dma desc bit */ +/* DMA descriptor bitfields */ +#define PHYTMAC_RXUSED_INDEX 0 +#define PHYTMAC_RXUSED_WIDTH 1 +#define PHYTMAC_RXWRAP_INDEX 1 +#define PHYTMAC_RXWRAP_WIDTH 1 +#define PHYTMAC_RXTSVALID_INDEX 2 +#define PHYTMAC_RXTSVALID_WIDTH 1 +#define PHYTMAC_RXWADDR_INDEX 2 +#define PHYTMAC_RXWADDR_WIDTH 30 + +#define PHYTMAC_RXFRMLEN_INDEX 0 +#define PHYTMAC_RXFRMLEN_WIDTH 12 +#define PHYTMAC_RXINDEX_INDEX 12 +#define PHYTMAC_RXINDEX_WIDTH 2 +#define PHYTMAC_RXSOF_INDEX 14 +#define PHYTMAC_RXSOF_WIDTH 1 +#define PHYTMAC_RXEOF_INDEX 15 +#define PHYTMAC_RXEOF_WIDTH 1 + +#define PHYTMAC_RXFRMLEN_MASK 0x1FFF +#define PHYTMAC_RXJFRMLEN_MASK 0x3FFF + +#define PHYTMAC_RXTYPEID_MATCH_INDEX 22 +#define PHYTMAC_RXTYPEID_MATCH_WIDTH 2 +#define PHYTMAC_RXCSUM_INDEX 22 +#define PHYTMAC_RXCSUM_WIDTH 2 + +/* Buffer descriptor constants */ +#define PHYTMAC_RXCSUM_NONE 0 +#define PHYTMAC_RXCSUM_IP 1 +#define PHYTMAC_RXCSUM_IP_TCP 2 +#define PHYTMAC_RXCSUM_IP_UDP 3 + +#define PHYTMAC_TXFRMLEN_INDEX 0 +#define PHYTMAC_TXFRMLEN_WIDTH 14 +#define PHYTMAC_TXLAST_INDEX 15 +#define PHYTMAC_TXLAST_WIDTH 1 +#define PHYTMAC_TXNOCRC_INDEX 16 +#define PHYTMAC_TXNOCRC_WIDTH 1 +#define PHYTMAC_MSSMFS_INDEX 16 +#define PHYTMAC_MSSMFS_WIDTH 14 +#define PHYTMAC_TXLSO_INDEX 17 +#define PHYTMAC_TXLSO_WIDTH 2 +#define PHYTMAC_TXTCP_SEQ_SRC_INDEX 19 +#define PHYTMAC_TXTCP_SEQ_SRC_WIDTH 1 +#define PHYTMAC_TXTSVALID_INDEX 23 +#define PHYTMAC_TXTSVALID_WIDTH 1 +#define PHYTMAC_TXWRAP_INDEX 30 +#define PHYTMAC_TXWRAP_WIDTH 1 +#define PHYTMAC_TXUSED_INDEX 31 +#define PHYTMAC_TXUSED_WIDTH 1 + +/* dma ts */ +#define PHYTMAC_TS_NSEC_INDEX 0 +#define PHYTMAC_TS_NSEC_WIDTH 30 +#define PHYTMAC_TS_SECL_INDEX 30 +#define PHYTMAC_TS_SECL_WIDTH 2 +#define PHYTMAC_TS_SECH_INDEX 0 +#define PHYTMAC_TS_SECH_WIDTH 4 +#define PHYTMAC_TS_SEC_MASK 0x3f +#define PHYTMAC_TS_SEC_TOP 0x40 + +#define HW_DMA_CAP_64B 0x1 +#define HW_DMA_CAP_CSUM 0x2 +#define HW_DMA_CAP_PTP 0x4 +#define HW_DMA_CAP_DDW64 0x8 +#define HW_DMA_CAP_DDW128 0x10 + +#define PHYTMAC_DBW64 2 +#define PHYTMAC_DBW128 4 + +enum phytmac_msg_cmd_id { + PHYTMAC_MSG_CMD_DEFAULT = 0, + PHYTMAC_MSG_CMD_SET, + PHYTMAC_MSG_CMD_GET, + PHYTMAC_MSG_CMD_DATA, + PHYTMAC_MSG_CMD_REPORT, +}; + +enum phytmac_default_subid { + PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 0, + PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE, + PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE, +}; + +enum phytmac_set_subid { + PHYTMAC_MSG_CMD_SET_INIT_ALL = 0, + PHYTMAC_MSG_CMD_SET_INIT_RING = 1, + PHYTMAC_MSG_CMD_SET_INIT_TX_RING = 2, + PHYTMAC_MSG_CMD_SET_INIT_RX_RING = 3, + PHYTMAC_MSG_CMD_SET_MAC_CONFIG = 4, + PHYTMAC_MSG_CMD_SET_ADDR = 5, + PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE = 6, + PHYTMAC_MSG_CMD_SET_DMA = 7, + PHYTMAC_MSG_CMD_SET_CAPS = 8, + PHYTMAC_MSG_CMD_SET_TS_CONFIG = 9, + PHYTMAC_MSG_CMD_SET_INIT_TX_ENABLE_TRANSMIT = 10, + PHYTMAC_MSG_CMD_SET_INIT_RX_ENABLE_RECEIVE = 11, + PHYTMAC_MSG_CMD_SET_ENABLE_NETWORK = 12, + PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK = 13, + PHYTMAC_MSG_CMD_SET_ENABLE_MDIO = 14, + PHYTMAC_MSG_CMD_SET_DISABLE_MDIO = 15, + PHYTMAC_MSG_CMD_SET_ENABLE_TXCSUM = 16, + PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM = 17, + PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM = 18, + PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM = 19, + PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE = 20, + PHYTMAC_MSG_CMD_SET_DISABLE_PROMISE = 21, + PHYTMAC_MSG_CMD_SET_ENABLE_MC = 22, + PHYTMAC_MSG_CMD_SET_DISABLE_MC = 23, + PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC = 24, + PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE = 25, + PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE = 26, + PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO = 27, + PHYTMAC_MSG_CMD_SET_DISABLE_JUMBO = 28, + PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES = 29, + PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC = 30, + PHYTMAC_MSG_CMD_SET_DISABLE_STRIPCRC = 31, + PHYTMAC_MSG_CMD_SET_PCS_LINK_UP = 32, + PHYTMAC_MSG_CMD_SET_PCS_LINK_DOWN = 33, + PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG = 34, + PHYTMAC_MSG_CMD_SET_REG_WRITE = 35, + PHYTMAC_MSG_CMD_SET_ENABLE_BC = 36, + PHYTMAC_MSG_CMD_SET_DISABLE_BC = 37, + PHYTMAC_MSG_CMD_SET_ETH_MATCH = 38, + PHYTMAC_MSG_CMD_SET_ADD_FDIR = 39, + PHYTMAC_MSG_CMD_SET_DEL_FDIR = 40, + PHYTMAC_MSG_CMD_SET_ENABLE_AUTONEG = 41, + PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG = 42, + PHYTMAC_MSG_CMD_SET_RX_DATA_OFFSET = 43, + PHYTMAC_MSG_CMD_SET_WOL = 44, +}; + +enum phytmac_get_subid { + PHYTMAC_MSG_CMD_GET_ADDR, + PHYTMAC_MSG_CMD_GET_QUEUENUMS, + PHYTMAC_MSG_CMD_GET_CAPS, + PHYTMAC_MSG_CMD_GET_BD_PREFETCH, + PHYTMAC_MSG_CMD_GET_STATS, + PHYTMAC_MSG_CMD_GET_REG_READ, +}; + +struct phytmac_interface_info { + u8 interface; + u8 autoneg; + u16 duplex; + u32 speed; +} __packed; + +struct phytmac_mc_info { + u32 mc_bottom; + u32 mc_top; +} __packed; + +struct phytmac_fdir_info { + u32 ip4src; + u32 ip4dst; + u16 srcport; + u16 srcport_mask; + u16 dstport; + u16 dstport_mask; + u8 location; + u8 queue; + u8 ipsrc_en; + u8 ipdst_en; + u8 port_en; +} __packed; + +struct phytmac_ts_config { + u8 tx_mode; + u8 rx_mode; + u8 one_step; +} __packed; + +struct phytmac_ring_info { + u64 addr[4]; + u8 queue_num; + u8 hw_dma_cap; +} __packed; + +struct phytmac_rxbuf_info { + u8 queue_num; + u8 buffer_size; +} __packed; + +struct phytmac_dma_info { + u16 dma_burst_length; + u8 hw_dma_cap; +} __packed; + +struct phytmac_eth_info { + u16 index; + u16 etype; +} __packed; + +struct phytmac_mac { + u32 addrl; + u16 addrh; +} __packed; + +struct phytmac_feature { + u8 irq_read_clear; + u8 dma_data_width; + u8 dma_addr_width; + u8 tx_pkt_buffer; + u8 rx_pkt_buffer; + u8 pbuf_lso; + u8 queue_num; + u8 tx_bd_prefetch; + u8 rx_bd_prefetch; + u8 max_rx_fs; +} __packed; + +struct phytmac_msg_info { + u16 module_id; + u16 cmd_id; + u16 cmd_subid; + u16 flags; + u8 para[64]; +} __packed; + +#endif From 3079b7a2bcc9d560ec3c8be8771836f36d0bb53e Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 29 Mar 2024 16:45:04 +0800 Subject: [PATCH 309/521] PHYTIUM: net/phytmac: Bugfixed the MDIO registration failures When MDIO registration fails, it will double free netdev, which will cause the system to crash. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I2605bc0ac4554c81b18e7874a68409e0a535da7a Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/593ac038cf31fbf629ed1c84176c349976e31272 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 31 +++++++++++++-------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index b0977f5cae755c..da4e0673628031 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1508,7 +1508,7 @@ int phytmac_mdio_register(struct phytmac *pdata) pdata->mii_bus = mdiobus_alloc(); if (!pdata->mii_bus) { ret = -ENOMEM; - goto free_mdio; + goto err_out; } pdata->mii_bus->name = "phytmac_mii_bus"; @@ -1519,7 +1519,7 @@ int phytmac_mdio_register(struct phytmac *pdata) if (pdata->platdev) { snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", - pdata->mii_bus->name, netdev_name(pdata->ndev)); + pdata->mii_bus->name, pdata->platdev->name); } else if (pdata->pcidev) { snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", pdata->mii_bus->name, pci_name(pdata->pcidev)); @@ -1536,6 +1536,9 @@ int phytmac_mdio_register(struct phytmac *pdata) return mdiobus_register(pdata->mii_bus); free_mdio: mdiobus_free(pdata->mii_bus); + pdata->mii_bus = NULL; + +err_out: return ret; } @@ -2043,30 +2046,26 @@ int phytmac_drv_probe(struct phytmac *pdata) ret = phytmac_init(pdata); if (ret) - goto err_out_free_netdev; + goto err_out; if (pdata->use_ncsi) { pdata->ncsidev = ncsi_register_dev(ndev, phytmac_ncsi_handler); if (!pdata->ncsidev) - goto err_out_free_netdev; + goto err_out; } netif_carrier_off(ndev); ret = register_netdev(ndev); if (ret) { dev_err(pdata->dev, "Cannot register net device, aborting.\n"); - goto err_out_free_netdev; + goto err_out; } - if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "probe success!Phytium %s at 0x%08lx irq %d (%pM)\n", - "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); - if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); if (ret) { netdev_err(ndev, "MDIO bus registration failed\n"); - goto err_phylink_init; + goto err_out_free_mdiobus; } } @@ -2076,15 +2075,23 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", + "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); + return 0; err_phylink_init: if (pdata->mii_bus) mdiobus_unregister(pdata->mii_bus); -err_out_free_netdev: - free_netdev(ndev); +err_out_free_mdiobus: + if (pdata->mii_bus) + mdiobus_free(pdata->mii_bus); + + unregister_netdev(ndev); +err_out: return ret; } EXPORT_SYMBOL_GPL(phytmac_drv_probe); From c1aed490bdee4bb63b75dea0c623f3ccb5853f2a Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 29 Mar 2024 16:46:54 +0800 Subject: [PATCH 310/521] PHYTIUM: net/phytmac: Fixed the issue of pxe startup failure. When network driver supports broadcast mode, no_broadcast bit of the network config register needs to be set to 0. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I4721a61da370130dc6dce608448681b3fe8298d3 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/82a2fb0b2765a621ca2617c2471856264f909332 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_v1.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 2d9f67c82050c9..79df9a7f981f1c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -357,8 +357,11 @@ static int phytmac_init_hw(struct phytmac *pdata) config |= PHYTMAC_BIT(PROMISC); if (pdata->ndev->features & NETIF_F_RXCSUM) config |= PHYTMAC_BIT(RCO_EN); - if (!(pdata->ndev->flags & IFF_BROADCAST)) + if (pdata->ndev->flags & IFF_BROADCAST) + config &= ~PHYTMAC_BIT(NO_BCAST); + else config |= PHYTMAC_BIT(NO_BCAST); + /* pause enable */ config |= PHYTMAC_BIT(PAUSE_EN); /* Rx Fcs remove */ From cf878dbfe8272fb9d598c8f72438f8d73ac63bb2 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 29 Mar 2024 16:49:17 +0800 Subject: [PATCH 311/521] PHYTIUM: net/phytmac: Adapt interface type 1000BASE-x Added 1000BASE-x interface type support for phymac driver. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I7f950dad763afd4814c73e0ca67d08168d5b2cb8 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b590f0e74c19a7da0dd49e0777600cb39d50bf84 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 1 + drivers/net/ethernet/phytium/phytmac_v1.c | 35 ++++++++++++++++++--- drivers/net/ethernet/phytium/phytmac_v1.h | 2 ++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index da4e0673628031..c172103a973628 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1560,6 +1560,7 @@ static void phytmac_validate(struct phylink_config *config, struct phytmac *pdata = netdev_priv(ndev); if (state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX && state->interface != PHY_INTERFACE_MODE_2500BASEX && state->interface != PHY_INTERFACE_MODE_5GBASER && state->interface != PHY_INTERFACE_MODE_10GBASER && diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 79df9a7f981f1c..ec95c6c79b0668 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -161,6 +161,20 @@ static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) return 0; } +static int phytmac_pcs_software_reset(struct phytmac *pdata, int reset) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + + if (reset) + value |= PHYTMAC_BIT(PCS_RESET); + else + value &= ~PHYTMAC_BIT(PCS_RESET); + + PHYTMAC_WRITE(pdata, PHYTMAC_PCSCTRL, value); + + return 0; +} + static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, int speed, int duplex) { @@ -401,6 +415,10 @@ static int phytmac_init_hw(struct phytmac *pdata) if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + + if (phy_interface_mode_is_8023z(pdata->phy_interface)) + phytmac_pcs_software_reset(pdata, 1); + return 0; } @@ -1047,9 +1065,16 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod | PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); ctrl &= ~(PHYTMAC_BIT(HIGHSPEED) | PHYTMAC_BIT(2PT5G)); - if (state->interface == PHY_INTERFACE_MODE_SGMII || - state->interface == PHY_INTERFACE_MODE_2500BASEX) { + if (state->interface == PHY_INTERFACE_MODE_SGMII) { config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); + if (state->speed == SPEED_1000) + config |= PHYTMAC_BIT(GM_EN); + else if (state->speed == SPEED_2500) + config |= PHYTMAC_BIT(2PT5G); + } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); + } else if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { + config |= PHYTMAC_BIT(2PT5G) | PHYTMAC_BIT(PCS_EN); } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || state->interface == PHY_INTERFACE_MODE_USXGMII || state->interface == PHY_INTERFACE_MODE_5GBASER) { @@ -1078,15 +1103,17 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ - if (state->interface == PHY_INTERFACE_MODE_SGMII || - state->interface == PHY_INTERFACE_MODE_2500BASEX) + if (state->interface == PHY_INTERFACE_MODE_SGMII) phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + phytmac_enable_autoneg(pdata, 1); } static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || interface == PHY_INTERFACE_MODE_2500BASEX) return PHYTMAC_READ_BITS(pdata, PHYTMAC_NSTATUS, PCS_LINK); else if (interface == PHY_INTERFACE_MODE_USXGMII || diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 6f2b521aaebdbb..d8de2c26cab4b2 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -228,6 +228,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; /* PCSCTRL register */ #define PHYTMAC_AUTONEG_INDEX 12 #define PHYTMAC_AUTONEG_WIDTH 1 +#define PHYTMAC_PCS_RESET_INDEX 15 +#define PHYTMAC_PCS_RESET_WIDTH 1 /* DEFAULT1 register */ #define PHYTMAC_DBW_INDEX 25 From 955fc506b170b76404456e7cb6f6d6cdf86f7982 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:33:32 +0800 Subject: [PATCH 312/521] PHYTIUM: net/phytmac:Added high address check A maximum of three DMA ring addresses can be allocated, and check whether the high addresses in multiple queues are consistent after the address is assigned. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ifd4d5c486341e1ed4cc1e433799bc53d15647f15 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/1ed603cd93296e432c6322f6fac2d968170eafd0 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 103 ++++++++++++++------ 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index c172103a973628..07f8476946bc60 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -46,6 +46,7 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define MAX_MTU 3072 #define RING_ADDR_INTERVAL 128 +#define MAX_RING_ADDR_ALLOC_TIMES 3 #define RX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc) \ * (pdata)->rx_ring_size) @@ -60,6 +61,23 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); * space in the SRAM (16KB) even when there is. */ +static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_base_addr, + int offset) +{ + u32 bus_addr_high; + int i; + + /* Check the high address of the DMA ring. */ + bus_addr_high = upper_32_bits(ring_base_addr); + for (i = 1; i < pdata->queues_num; i++) { + ring_base_addr += offset; + if (bus_addr_high != upper_32_bits(ring_base_addr)) + return -EFAULT; + } + + return 0; +} + static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) { if (netif_running(ndev)) @@ -176,7 +194,6 @@ static int phytmac_mdio_write_c45(struct mii_bus *bus, int mii_id, int devad, in return 0; } - static inline int hash_bit_value(int bitnr, __u8 *addr) { if (addr[bitnr / 8] & (1 << (bitnr % 8))) @@ -397,19 +414,36 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) struct phytmac_dma_desc *tx_ring_base; dma_addr_t tx_ring_base_addr; unsigned int q; - int size; + int tx_offset; + int tx_size; + int size = 0; + int ret, i; + + tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_size = pdata->queues_num * tx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + goto err; + + tx_ring_base = dma_alloc_coherent(pdata->dev, tx_size, + &tx_ring_base_addr, GFP_KERNEL); + if (!tx_ring_base) + continue; - size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + - RING_ADDR_INTERVAL); - tx_ring_base = dma_alloc_coherent(pdata->dev, size, - &tx_ring_base_addr, GFP_KERNEL); - if (!tx_ring_base) - goto err; + ret = phytmac_queue_phyaddr_check(pdata, tx_ring_base_addr, + tx_offset); + if (ret) { + dma_free_coherent(pdata->dev, tx_size, tx_ring_base, + tx_ring_base_addr); + continue; + } else { + break; + } + } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - size = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; - queue->tx_ring = (void *)tx_ring_base + q * size; - queue->tx_ring_addr = tx_ring_base_addr + q * size; + queue->tx_ring = (void *)tx_ring_base + q * tx_offset; + queue->tx_ring_addr = tx_ring_base_addr + q * tx_offset; if (!queue->tx_ring) goto err; @@ -428,7 +462,6 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) "Allocated %d TX struct tx_skb entries at %p\n", pdata->tx_ring_size, queue->tx_skb); } - tx_ring_base = NULL; return 0; err: @@ -443,21 +476,37 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base; dma_addr_t rx_ring_base_addr; + int rx_offset; + int rx_size; unsigned int q; - int size; - int i; + int size = 0; + int ret, i; - size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + - RING_ADDR_INTERVAL); - rx_ring_base = dma_alloc_coherent(pdata->dev, size, - &rx_ring_base_addr, GFP_KERNEL); - if (!rx_ring_base) - goto err; + rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_size = pdata->queues_num * rx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + goto err; + + rx_ring_base = dma_alloc_coherent(pdata->dev, rx_size, + &rx_ring_base_addr, GFP_KERNEL); + if (!rx_ring_base) + continue; + + ret = phytmac_queue_phyaddr_check(pdata, rx_ring_base_addr, + rx_offset); + if (ret) { + dma_free_coherent(pdata->dev, rx_size, rx_ring_base, + rx_ring_base_addr); + continue; + } else { + break; + } + } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - size = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; - queue->rx_ring = (void *)rx_ring_base + q * size; - queue->rx_ring_addr = rx_ring_base_addr + q * size; + queue->rx_ring = (void *)rx_ring_base + q * rx_offset; + queue->rx_ring_addr = rx_ring_base_addr + q * rx_offset; if (!queue->rx_ring) goto err; @@ -479,7 +528,6 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) "Allocated %d RX struct sk_buff entries at %p\n", pdata->rx_ring_size, queue->rx_skb); } - rx_ring_base = NULL; return 0; err: @@ -763,7 +811,7 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) } paddr = dma_map_single(pdata->dev, skb->data, - pdata->rx_buffer_len, DMA_FROM_DEVICE); + pdata->rx_buffer_len, DMA_FROM_DEVICE); if (dma_mapping_error(pdata->dev, paddr)) { dev_kfree_skb(skb); break; @@ -1368,14 +1416,12 @@ void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_modes(interface), speed, duplex); hw_if->pcs_linkup(pdata, interface, speed, duplex); } - static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { .pcs_config = phytmac_pcs_config, .pcs_link_up = phytmac_pcs_link_up, }; - static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, - phy_interface_t interface) + phy_interface_t interface) { struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); @@ -1391,7 +1437,6 @@ static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, return &pdata->phylink_pcs; } - static void phytmac_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { From 1448baf9ab97810d0d3d7deb2bf5e13d723a4add Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:52:58 +0800 Subject: [PATCH 313/521] PHYTIUM: net/phytmac: Adjust the code style Fix spelling mistakes and replace the default value with a macro definition. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Icc51d45f0fb10eb8dad8aad59f839f2c0406653c Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/6848fd7516a4ab90e013a42513bb8c8b6a8b6cf8 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 4 +++- drivers/net/ethernet/phytium/phytmac_v1.c | 4 ++-- drivers/net/ethernet/phytium/phytmac_v1.h | 8 ++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 07f8476946bc60..ca1a03fa355262 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1416,10 +1416,12 @@ void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_modes(interface), speed, duplex); hw_if->pcs_linkup(pdata, interface, speed, duplex); } + static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { .pcs_config = phytmac_pcs_config, .pcs_link_up = phytmac_pcs_link_up, }; + static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) { @@ -1766,7 +1768,7 @@ static int phytmac_open(struct net_device *ndev) ret = phytmac_phylink_connect(pdata); if (ret) { - netdev_err(ndev, "phylink connet failed,(error %d)\n", + netdev_err(ndev, "phylink connect failed,(error %d)\n", ret); goto reset_hw; } diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index ec95c6c79b0668..4a690b6a96cfe8 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -414,7 +414,7 @@ static int phytmac_init_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, PHYTMAC_BIT(TXTAIL_ENABLE)); if (phy_interface_mode_is_8023z(pdata->phy_interface)) phytmac_pcs_software_reset(pdata, 1); @@ -945,7 +945,7 @@ static void phytmac_tx_start(struct phytmac_queue *queue) if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), - BIT(31) | queue->tx_tail); + PHYTMAC_BIT(TXTAIL_UPDATE) | queue->tx_tail); if (pdata->capacities & PHYTMAC_CAPS_START) PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index d8de2c26cab4b2..2cabadfed2f11f 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -356,6 +356,14 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_VLAN_ID_INDEX 10 #define PHYTMAC_VLAN_ID_WIDTH 1 +/* Bitfields in TAILPTR */ +#define PHYTMAC_TXTAIL_UPDATE_INDEX 31 /* Update tx tail */ +#define PHYTMAC_TXTAIL_UPDATE_WIDTH 1 + +/* Bitfields in TAIL_ENABLE */ +#define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) From b53d1a15b6dc1aae423b7f578c714b3ff86d6077 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:55:39 +0800 Subject: [PATCH 314/521] PHYTIUM: net/phytmac: Get device type error fixed We should return -EOPNOTSUPP to the unsupported device types, otherwise the wireless attribute will be added by default. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie0522ed5c744ddc885ab633dac5ecc9661c580f2 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/66403a1703bf00c249e081103f7efbc2867c53bc Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index ca1a03fa355262..058f9647b85516 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1843,7 +1843,7 @@ static int phytmac_close(struct net_device *ndev) static int phytmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct phytmac *pdata = netdev_priv(dev); - int ret; + int ret = -EOPNOTSUPP; if (!netif_running(dev)) return -EINVAL; From a3f0945f3b254ea1ef3abd9b455d64953d792c35 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:57:15 +0800 Subject: [PATCH 315/521] PHYTIUM: net/phytmac: Modify the method of obtaining the link status Firstly, modify the state of pdata->speed and pdata->duplex to be obtained in the phytmac_mac_config. Secondly, the base.speed and base.duplex in the phytmac_get_link_ ksettings were changed from the default values to pdata. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ifda2028cfd32381f97b57ad1a46f0861c0c21105 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/a728bf473513902c773a61d68ea35a1dd08c39d6 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../net/ethernet/phytium/phytmac_ethtool.c | 45 +++++++------------ drivers/net/ethernet/phytium/phytmac_pci.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 1 + 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 592d2d9dc6d4b2..7cddb01802f217 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -382,43 +382,32 @@ static int phytmac_get_link_ksettings(struct net_device *ndev, u32 advertising = 0; if (!ndev->phydev) { + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = pdata->duplex; + kset->base.speed = pdata->speed; + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; advertising = ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_10000; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - supported = SUPPORTED_2500baseX_Full | SUPPORTED_Pause; - advertising = ADVERTISED_2500baseX_Full | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_2500; + supported = SUPPORTED_2500baseX_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) { - supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full - | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE - | SUPPORTED_Pause; - advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full - | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE - | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_100; + supported = SUPPORTED_1000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII) { - supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full - | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; - advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full - | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_1000; + supported = SUPPORTED_1000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index fd21bf80f13883..af69329fe06de5 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -225,7 +225,7 @@ struct phytmac_data phytmac_1000basex = { .use_mii = false, .speed = 1000, .duplex = true, - .interface = PHY_INTERFACE_MODE_SGMII, + .interface = PHY_INTERFACE_MODE_1000BASEX, .properties = fl_properties[0], }; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index df142aa6797f68..41e5df41226e96 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -842,6 +842,7 @@ static int phytmac_pcs_linkdown(struct phytmac *pdata) static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || interface == PHY_INTERFACE_MODE_2500BASEX) return PHYTMAC_READ_BITS(pdata, PHYTMAC_NETWORK_STATUS, LINK); else if (interface == PHY_INTERFACE_MODE_USXGMII || From 18be6b2f8559185b4fa61157396440289484c901 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:02:13 +0800 Subject: [PATCH 316/521] PHYTIUM: net/phytmac: Support usxgmii wol feature Support usxgmii wol by change the value of registers, including wol register, int register, net config register and spec_add1 register. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie651779a4bc2487aaaa51b24c69a5cc9d8de3784 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/11ba74ddc3fcd568a41284b08ff69a86809e38fa Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 12 ++++++------ drivers/net/ethernet/phytium/phytmac_v1.c | 9 +++++++++ drivers/net/ethernet/phytium/phytmac_v1.h | 11 +++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 058f9647b85516..28b6dc86f6688f 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1732,12 +1732,6 @@ static int phytmac_open(struct net_device *ndev) hw_if->reset_hw(pdata); - ret = phytmac_get_mac_address(pdata); - if (ret) { - netdev_err(ndev, "phytmac get mac address failed\n"); - goto reset_hw; - } - ret = netif_set_real_num_tx_queues(ndev, pdata->queues_num); if (ret) { netdev_err(ndev, "error setting real tx queue number\n"); @@ -2123,6 +2117,12 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + ret = phytmac_get_mac_address(pdata); + if (ret) { + netdev_err(ndev, "phytmac get mac address failed\n"); + goto err_phylink_init; + } + if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 4a690b6a96cfe8..b96547e54d822c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -489,6 +489,15 @@ static int phytmac_set_wake(struct phytmac *pdata, int wake) value |= PHYTMAC_BIT(MCAST); PHYTMAC_WRITE(pdata, PHYTMAC_WOL, value); + if (wake) { + PHYTMAC_WRITE(pdata, PHYTMAC_IE, PHYTMAC_BIT(WOL_RECEIVE_ENABLE)); + value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG) | PHYTMAC_BIT(IGNORE_RX_FCS); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_ID, PHYTMAC_BIT(WOL_RECEIVE_DISABLE)); + value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG) & ~PHYTMAC_BIT(IGNORE_RX_FCS); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + } return 0; } diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 2cabadfed2f11f..1f49d4ec79a04a 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -140,6 +140,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_DBW_128 4 #define PHYTMAC_RCO_EN_INDEX 24 /* Receive checksum offload enable */ #define PHYTMAC_RCO_EN_WIDTH 1 +#define PHYTMAC_IGNORE_RX_FCS_INDEX 26 +#define PHYTMAC_IGNORE_RX_FCS_WIDTH 1 #define PHYTMAC_SGMII_EN_INDEX 27 /* Sgmii mode enable */ #define PHYTMAC_SGMII_EN_WIDTH 1 @@ -364,6 +366,15 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ #define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 +/* Bitfields in INT ENABLE */ +#define PHYTMAC_WOL_RECEIVE_ENABLE_INDEX 28 /* Enable wol_event_recieve */ +#define PHYTMAC_WOL_RECEIVE_ENABLE_WIDTH 1 + +/* Bitfields in INT DISABLE */ +#define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_recieve */ +#define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 + + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) From 8422f7746934a78e0692333243d321bcb43df677 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:04:49 +0800 Subject: [PATCH 317/521] PHYTIUM: net/phytmac: Roll back rx_clean version Roll back rx_clean version to not use batching. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie6e7585304ac6a7b9101e04c5af5219b3a361996 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/1040f4e7b52143571a6fa9e85e13cd0c1642aafc Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 -- drivers/net/ethernet/phytium/phytmac_main.c | 24 ++++++++------------- drivers/net/ethernet/phytium/phytmac_v1.c | 23 ++++++-------------- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index b90e1551499ef7..33c0fc78415b25 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -38,7 +38,6 @@ #define MIN_TX_RING_SIZE 64 #define MIN_RX_RING_SIZE 64 #define DEFAULT_TX_DESC_MIN_FREE 64 -#define DEFAULT_RX_DESC_MIN_FREE 64 #define MEMORY_SIZE 4096 #define MHU_SIZE 0x20 @@ -499,7 +498,6 @@ struct phytmac_hw_if { struct packet_info *packet); void (*init_rx_map)(struct phytmac_queue *queue, u32 index); unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); - unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); void (*transmit)(struct phytmac_queue *queue); void (*restart)(struct phytmac *pdata); int (*tx_complete)(const struct phytmac_dma_desc *desc); diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 28b6dc86f6688f..0053412c2632c5 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -793,16 +793,12 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) unsigned int index, space; dma_addr_t paddr; struct sk_buff *skb; - unsigned int rx_unclean = 0; space = CIRC_SPACE(queue->rx_head, queue->rx_tail, pdata->rx_ring_size); - if (space < DEFAULT_RX_DESC_MIN_FREE) - return; - - index = queue->rx_head & (pdata->rx_ring_size - 1); while (space > 0) { + index = queue->rx_head & (pdata->rx_ring_size - 1); if (!queue->rx_skb[index]) { skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); if (unlikely(!skb)) { @@ -811,7 +807,7 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) } paddr = dma_map_single(pdata->dev, skb->data, - pdata->rx_buffer_len, DMA_FROM_DEVICE); + pdata->rx_buffer_len, DMA_FROM_DEVICE); if (dma_mapping_error(pdata->dev, paddr)) { dev_kfree_skb(skb); break; @@ -820,21 +816,19 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) queue->rx_skb[index] = skb; hw_if->rx_map(queue, index, paddr); + } else { + hw_if->rx_map(queue, index, 0); } - index = (index + 1) & (pdata->rx_ring_size - 1); - rx_unclean++; + queue->rx_head++; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); + space--; } /* make newly descriptor to hardware */ wmb(); - hw_if->rx_clean(queue, rx_unclean); - /* make newly descriptor to hardware */ - wmb(); - queue->rx_head += rx_unclean; - if (queue->rx_head >= pdata->rx_ring_size) - queue->rx_head &= (pdata->rx_ring_size - 1); } static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, @@ -1203,7 +1197,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, { dma_addr_t mapping; struct phytmac_hw_if *hw_if = pdata->hw_if; - unsigned int len, i, tx_tail = queue->tx_tail; + unsigned int len, i, tx_tail; struct phytmac_tx_skb *tx_skb = NULL; unsigned int offset, size, count = 0; unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index b96547e54d822c..b823a07a731b67 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -927,22 +927,14 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, addr |= PHYTMAC_BIT(RX_WRAP); desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); - desc->desc0 = lower_32_bits(addr) | PHYTMAC_BIT(RX_USED); - } - return 0; -} - -static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count) -{ - struct phytmac_dma_desc *desc; - u32 index = queue->rx_head + count - 1; - - while (count) { - desc = phytmac_get_rx_desc(queue, index); - desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* Make newly descriptor to hardware */ dma_wmb(); - index--; - count--; + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); } return 0; @@ -1386,7 +1378,6 @@ struct phytmac_hw_if phytmac_1p0_hw = { .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, - .rx_clean = phytmac_rx_clean_desc, .rx_checksum = phytmac_rx_checksum, .rx_single_buffer = phytmac_rx_single_buffer, .rx_pkt_start = phytmac_rx_sof, From b6997176e313f0a180ecf3338a6ebd812cde66b6 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:09:08 +0800 Subject: [PATCH 318/521] PHYTIUM: net/phytmac: Override rx_skb allocation function Changing the way netdev_alloc_skb function assigns SKB directly, so that MAC driver alloc pages in advance and build SKB in the phytmac RX function. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ia765cea707d97a212ab9864c139a6198668c8604 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/bb1fd376f46bcf15d08af0210af4b45ca5990fcb Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 24 +- drivers/net/ethernet/phytium/phytmac_main.c | 432 +++++++++++++++----- drivers/net/ethernet/phytium/phytmac_v1.c | 11 - drivers/net/ethernet/phytium/phytmac_v2.c | 11 - 4 files changed, 341 insertions(+), 137 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 33c0fc78415b25..363bc65c72d474 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -307,6 +307,13 @@ struct phytmac_tx_ts { u32 ts_2; }; +struct phytmac_rx_buffer { + dma_addr_t addr; + struct page *page; + __u16 page_offset; + __u16 pagecnt_bias; +}; + struct phytmac_queue { struct phytmac *pdata; int irq; @@ -328,8 +335,9 @@ struct phytmac_queue { dma_addr_t rx_ring_addr; unsigned int rx_head; unsigned int rx_tail; + unsigned int rx_next_to_alloc; struct phytmac_dma_desc *rx_ring; - struct sk_buff **rx_skb; + struct phytmac_rx_buffer *rx_buffer_info; struct napi_struct rx_napi; struct phytmac_queue_stats stats; @@ -503,7 +511,6 @@ struct phytmac_hw_if { int (*tx_complete)(const struct phytmac_dma_desc *desc); int (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); - dma_addr_t (*get_desc_addr)(const struct phytmac_dma_desc *desc); bool (*rx_checksum)(const struct phytmac_dma_desc *desc); void (*set_desc_rxused)(struct phytmac_dma_desc *desc); bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); @@ -572,6 +579,19 @@ struct phytmac_hw_if { #define PHYTMAC_READ_DATA0(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA0) #define PHYTMAC_TIMEOUT 1000000000 /* in usecs */ +#define PHYTMAC_GFP_FLAGS \ + (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA | __GFP_DMA32) +#define PHYTMAC_RX_DMA_ATTR \ + (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) +#define PHYTMAC_SKB_PAD (NET_SKB_PAD) + +#define PHYTMAC_RXBUFFER_2048 2048 +#define PHYTMAC_MAX_FRAME_BUILD_SKB \ + (SKB_WITH_OVERHEAD(PHYTMAC_RXBUFFER_2048) - PHYTMAC_SKB_PAD) + +#define PHYTMAC_RX_PAGE_ORDER 0 +#define PHYTMAC_RX_PAGE_SIZE (PAGE_SIZE << PHYTMAC_RX_PAGE_ORDER) + struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index); inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 0053412c2632c5..a2b3ed84e895a9 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "phytmac.h" #include "phytmac_ptp.h" @@ -293,15 +295,12 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) return nstat; } -static int phytmac_calc_rx_buf_len(struct phytmac *pdata, u32 mtu) +static inline int phytmac_calc_rx_buf_len(void) { - unsigned int size = mtu + ETH_HLEN + ETH_FCS_LEN; - int rx_buf_len = roundup(size, RX_BUFFER_MULTIPLE); - - netdev_dbg(pdata->ndev, "mtu [%u] rx_buffer_size [%u]\n", - mtu, rx_buf_len); - - return rx_buf_len; +#if (PAGE_SIZE < 8192) + return PHYTMAC_MAX_FRAME_BUILD_SKB; +#endif + return PHYTMAC_RXBUFFER_2048; } inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, @@ -310,12 +309,6 @@ inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, return &queue->rx_ring[index & (queue->pdata->rx_ring_size - 1)]; } -struct sk_buff *phytmac_get_rx_skb(struct phytmac_queue *queue, - unsigned int index) -{ - return queue->rx_skb[index & (queue->pdata->rx_ring_size - 1)]; -} - struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index) { @@ -328,6 +321,49 @@ inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, return &queue->tx_ring[index & (queue->pdata->tx_ring_size - 1)]; } +static void phytmac_rx_unmap(struct phytmac_queue *queue) +{ + struct phytmac_rx_buffer *rx_buffer_info; + struct phytmac *pdata = queue->pdata; + int i; + + if (queue->rx_buffer_info) { + /* Free all the Rx ring sk_buffs */ + i = queue->rx_tail; + + while (i != queue->rx_next_to_alloc) { + rx_buffer_info = &queue->rx_buffer_info[i]; + + /* Invalidate cache lines that may have been written to by + * device so that we avoid corrupting memory. + */ + dma_sync_single_range_for_cpu(pdata->dev, + rx_buffer_info->addr, + rx_buffer_info->page_offset, + pdata->rx_buffer_len, + DMA_FROM_DEVICE); + + /* free resources associated with mapping */ + dma_unmap_page_attrs(pdata->dev, + rx_buffer_info->addr, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, + PHYTMAC_RX_DMA_ATTR); + + __page_frag_cache_drain(rx_buffer_info->page, + rx_buffer_info->pagecnt_bias); + + i++; + if (i == pdata->rx_ring_size) + i = 0; + } + + queue->rx_tail = 0; + queue->rx_head = 0; + queue->rx_next_to_alloc = 0; + } +} + static int phytmac_free_tx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; @@ -362,14 +398,10 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) static int phytmac_free_rx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; - struct sk_buff *skb; - struct phytmac_dma_desc *desc; - struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base = NULL; dma_addr_t rx_ring_base_addr; - dma_addr_t addr; unsigned int q; - int size, i; + int size; queue = pdata->queues; if (queue->rx_ring) { @@ -378,25 +410,15 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - if (queue->rx_skb) { - for (i = 0; i < pdata->rx_ring_size; i++) { - skb = phytmac_get_rx_skb(queue, i); - if (skb) { - desc = &queue->rx_ring[i]; - addr = hw_if->get_desc_addr(desc); - dma_unmap_single(pdata->dev, addr, pdata->rx_buffer_len, - DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); - skb = NULL; - } - } - - kfree(queue->rx_skb); - queue->rx_skb = NULL; - } + phytmac_rx_unmap(queue); if (queue->rx_ring) queue->rx_ring = NULL; + + if (queue->rx_buffer_info) { + vfree(queue->rx_buffer_info); + queue->rx_buffer_info = NULL; + } } if (rx_ring_base) { @@ -450,7 +472,7 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "Allocated TX ring for queue %u of %d bytes at %08lx\n", - q, size, (unsigned long)queue->tx_ring_addr); + q, tx_offset, (unsigned long)queue->tx_ring_addr); size = pdata->tx_ring_size * sizeof(struct phytmac_tx_skb); queue->tx_skb = kzalloc(size, GFP_KERNEL); @@ -473,7 +495,6 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) static int phytmac_alloc_rx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; - struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base; dma_addr_t rx_ring_base_addr; int rx_offset; @@ -513,20 +534,12 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "Allocated RX ring for queue %u of %d bytes at %08lx\n", - q, size, (unsigned long)queue->rx_ring_addr); + q, rx_offset, (unsigned long)queue->rx_ring_addr); - for (i = 0; i < pdata->rx_ring_size; i++) - hw_if->init_rx_map(queue, i); - - size = pdata->rx_ring_size * sizeof(struct sk_buff *); - queue->rx_skb = kzalloc(size, GFP_KERNEL); - if (!queue->rx_skb) + size = pdata->rx_ring_size * sizeof(struct phytmac_rx_buffer); + queue->rx_buffer_info = vzalloc(size); + if (!queue->rx_buffer_info) goto err; - - if (netif_msg_drv(pdata)) - netdev_info(pdata->ndev, - "Allocated %d RX struct sk_buff entries at %p\n", - pdata->rx_ring_size, queue->rx_skb); } return 0; @@ -538,10 +551,9 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) static int phytmac_alloc_resource(struct phytmac *pdata) { - struct net_device *ndev = pdata->ndev; int ret; - pdata->rx_buffer_len = phytmac_calc_rx_buf_len(pdata, ndev->mtu); + pdata->rx_buffer_len = phytmac_calc_rx_buf_len(); if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "alloc resource, rx_buffer_len = %d\n", @@ -652,30 +664,226 @@ static void phytmac_dump_pkt(struct phytmac *pdata, struct sk_buff *skb, bool tx skb->data, skb->len, true); } +static bool phytmac_alloc_mapped_page(struct phytmac *pdata, + struct phytmac_rx_buffer *bi) +{ + struct page *page = bi->page; + dma_addr_t dma; + + /* since we are recycling buffers we should seldom need to alloc */ + if (likely(page)) + return true; + + /* alloc new page for storage */ + page = __dev_alloc_pages(PHYTMAC_GFP_FLAGS, PHYTMAC_RX_PAGE_ORDER); + if (unlikely(!page)) { + netdev_err(pdata->ndev, "rx alloc page failed\n"); + return false; + } + + /* map page for use */ + dma = dma_map_page_attrs(pdata->dev, page, 0, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, PHYTMAC_RX_DMA_ATTR); + if (dma_mapping_error(pdata->dev, dma)) { + __free_pages(page, PHYTMAC_RX_PAGE_ORDER); + return false; + } + + bi->addr = dma; + bi->page = page; + bi->page_offset = PHYTMAC_SKB_PAD; + bi->pagecnt_bias = 1; + + return true; +} + +static inline bool phytmac_page_is_reserved(struct page *page) +{ + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); +} + +static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) +{ + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; + struct page *page = rx_buffer->page; + + /* avoid re-using remote pages */ + if (unlikely(phytmac_page_is_reserved(page))) + return false; + +#if (PAGE_SIZE < 8192) + /* if we are only owner of page we can reuse it */ + if (unlikely((page_ref_count(page) - pagecnt_bias) > 1)) + return false; +#else +#define PHYTMAC_LAST_OFFSET \ + (SKB_WITH_OVERHEAD(PAGE_SIZE) - PHYTMAC_RXBUFFER_2048) + + if (rx_buffer->page_offset > PHYTMAC_LAST_OFFSET) + return false; +#endif + + /* If we have drained the page fragment pool we need to update + * the pagecnt_bias and page count so that we fully restock the + * number of references the driver holds. + */ + if (unlikely(!pagecnt_bias)) { + page_ref_add(page, USHRT_MAX); + rx_buffer->pagecnt_bias = USHRT_MAX; + } + + return true; +} + +static void phytmac_reuse_rx_page(struct phytmac_queue *queue, + struct phytmac_rx_buffer *old_buff) +{ + struct phytmac_rx_buffer *new_buff; + struct phytmac *pdata = queue->pdata; + u16 nta = queue->rx_next_to_alloc; + + new_buff = &queue->rx_buffer_info[nta & (pdata->rx_ring_size - 1)]; + + /* update, and store next to alloc */ + nta++; + queue->rx_next_to_alloc = (nta < pdata->rx_ring_size) ? nta : 0; + + /* Transfer page from old buffer to new buffer. + * Move each member individually to avoid possible store + * forwarding stalls. + */ + new_buff->addr = old_buff->addr; + new_buff->page = old_buff->page; + new_buff->page_offset = old_buff->page_offset; + new_buff->pagecnt_bias = old_buff->pagecnt_bias; +} + +static struct phytmac_rx_buffer *phytmac_get_rx_buffer(struct phytmac_queue *queue, + unsigned int index, + const unsigned int size) +{ + struct phytmac_rx_buffer *rx_buffer; + struct phytmac *pdata = queue->pdata; + + rx_buffer = &queue->rx_buffer_info[index & (pdata->rx_ring_size - 1)]; + prefetchw(rx_buffer->page); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(pdata->dev, + rx_buffer->addr, + rx_buffer->page_offset, + size, + DMA_FROM_DEVICE); + + rx_buffer->pagecnt_bias--; + + return rx_buffer; +} + +static void phytmac_put_rx_buffer(struct phytmac_queue *queue, + struct phytmac_rx_buffer *rx_buffer) +{ + struct phytmac *pdata = queue->pdata; + + if (phytmac_can_reuse_rx_page(rx_buffer)) { + /* hand second half of page back to the ring */ + phytmac_reuse_rx_page(queue, rx_buffer); + } else { + dma_unmap_page_attrs(pdata->dev, rx_buffer->addr, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, PHYTMAC_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + } + + /* clear contents of rx_buffer */ + rx_buffer->page = NULL; +} + +static void phytmac_add_rx_frag(struct phytmac_queue *queue, + struct phytmac_rx_buffer *rx_buffer, + struct sk_buff *skb, + unsigned int size) +{ + unsigned int truesize; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; +#else + truesize = SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); +#endif + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif +} + +static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, + struct phytmac_dma_desc *desc, + unsigned int size) +{ + struct sk_buff *skb; + unsigned int truesize; + void *va; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; +#else + truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); +#endif + + va = page_address(rx_buffer->page) + rx_buffer->page_offset; + /* prefetch first cache line of first page */ + prefetch(va); + + /* build an skb around the page buffer */ + skb = build_skb(va - PHYTMAC_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, PHYTMAC_SKB_PAD); + __skb_put(skb, size); + + /* update buffer offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + + return skb; +} + static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) { struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; - struct sk_buff *skb; + struct phytmac_rx_buffer *rx_buffer; + struct sk_buff *skb = NULL; unsigned int len; - dma_addr_t addr; - skb = phytmac_get_rx_skb(queue, queue->rx_tail); + len = hw_if->get_rx_pkt_len(pdata, desc); + rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + + skb = phytmac_build_skb(rx_buffer, desc, len); if (unlikely(!skb)) { netdev_err(pdata->ndev, - "inconsistent Rx descriptor chain\n"); + "rx single build skb failed\n"); pdata->ndev->stats.rx_dropped++; queue->stats.rx_dropped++; + rx_buffer->pagecnt_bias++; return NULL; } - queue->rx_skb[queue->rx_tail & (pdata->rx_ring_size - 1)] = NULL; - len = hw_if->get_rx_pkt_len(pdata, desc); - addr = hw_if->get_desc_addr(desc); + phytmac_put_rx_buffer(queue, rx_buffer); - skb_put(skb, len); - dma_unmap_single(pdata->dev, addr, - pdata->rx_buffer_len, DMA_FROM_DEVICE); skb->protocol = eth_type_trans(skb, pdata->ndev); skb_checksum_none_assert(skb); @@ -691,64 +899,65 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy } static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, - unsigned int first_frag, unsigned int last_frag, int len) + unsigned int first_frag, unsigned int last_frag, + unsigned int total_len) { - unsigned int offset = 0; unsigned int frag = 0; - unsigned int entry = 0; - dma_addr_t addr = 0; struct sk_buff *skb; struct phytmac_dma_desc *desc; struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int frag_len = pdata->rx_buffer_len; + unsigned int offset = frag_len; + struct phytmac_rx_buffer *rx_buffer; if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "rx frame %u - %u (len %u)\n", - first_frag, last_frag, len); + first_frag, last_frag, total_len); + + desc = phytmac_get_rx_desc(queue, first_frag); + rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); - skb = netdev_alloc_skb(pdata->ndev, len); - if (!skb) { + skb = phytmac_build_skb(rx_buffer, desc, frag_len); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, "rx frame build skb failed\n"); pdata->ndev->stats.rx_dropped++; - netdev_err(pdata->ndev, "rx frame alloc skb failed\n"); + queue->stats.rx_dropped++; + rx_buffer->pagecnt_bias++; return NULL; } - skb_checksum_none_assert(skb); + phytmac_put_rx_buffer(queue, rx_buffer); - if (pdata->ndev->features & NETIF_F_RXCSUM && - !(pdata->ndev->flags & IFF_PROMISC) && - hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) - skb->ip_summed = CHECKSUM_UNNECESSARY; - - skb_put(skb, len); + for (frag = first_frag + 1; ; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + rx_buffer = phytmac_get_rx_buffer(queue, frag, frag_len); - for (frag = first_frag; ; frag++) { - if (offset + frag_len > len) { + if (offset + frag_len > total_len) { if (unlikely(frag != last_frag)) { dev_kfree_skb_any(skb); + phytmac_put_rx_buffer(queue, rx_buffer); return NULL; } - frag_len = len - offset; + frag_len = total_len - offset; } - desc = phytmac_get_rx_desc(queue, frag); - addr = hw_if->get_desc_addr(desc); - dma_sync_single_for_cpu(pdata->dev, addr, frag_len, - DMA_FROM_DEVICE); - - entry = frag & (pdata->rx_ring_size - 1); - skb_copy_to_linear_data_offset(skb, offset, queue->rx_skb[entry]->data, frag_len); - - offset += pdata->rx_buffer_len; + phytmac_add_rx_frag(queue, rx_buffer, skb, frag_len); + phytmac_put_rx_buffer(queue, rx_buffer); - dma_sync_single_for_device(pdata->dev, addr, frag_len, - DMA_FROM_DEVICE); + offset += frag_len; if (frag == last_frag) break; } + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->protocol = eth_type_trans(skb, pdata->ndev); if (netif_msg_pktdata(pdata)) phytmac_dump_pkt(pdata, skb, false); @@ -764,10 +973,13 @@ static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) struct sk_buff *skb = NULL; unsigned int rx_tail = 0; int first_frag = -1; - int len; + unsigned int len; for (rx_tail = queue->rx_tail; ; rx_tail++) { desc = phytmac_get_rx_desc(queue, rx_tail); + if (!hw_if->rx_complete(desc)) + return NULL; + if (hw_if->rx_pkt_start(desc)) { if (first_frag != -1) hw_if->clear_rx_desc(queue, first_frag, rx_tail); @@ -791,34 +1003,24 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int index, space; - dma_addr_t paddr; - struct sk_buff *skb; + struct phytmac_rx_buffer *rx_buf_info; space = CIRC_SPACE(queue->rx_head, queue->rx_tail, pdata->rx_ring_size); while (space > 0) { index = queue->rx_head & (pdata->rx_ring_size - 1); - if (!queue->rx_skb[index]) { - skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); - if (unlikely(!skb)) { - netdev_err(pdata->ndev, "rx clean alloc skb failed\n"); - break; - } + rx_buf_info = &queue->rx_buffer_info[index]; - paddr = dma_map_single(pdata->dev, skb->data, - pdata->rx_buffer_len, DMA_FROM_DEVICE); - if (dma_mapping_error(pdata->dev, paddr)) { - dev_kfree_skb(skb); - break; - } - - queue->rx_skb[index] = skb; + if (!phytmac_alloc_mapped_page(pdata, rx_buf_info)) + break; + /* sync the buffer for use by the device */ + dma_sync_single_range_for_device(pdata->dev, rx_buf_info->addr, + rx_buf_info->page_offset, + pdata->rx_buffer_len, + DMA_FROM_DEVICE); - hw_if->rx_map(queue, index, paddr); - } else { - hw_if->rx_map(queue, index, 0); - } + hw_if->rx_map(queue, index, rx_buf_info->addr + rx_buf_info->page_offset); queue->rx_head++; if (queue->rx_head >= pdata->rx_ring_size) @@ -827,6 +1029,7 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) space--; } + queue->rx_next_to_alloc = queue->rx_head; /* make newly descriptor to hardware */ wmb(); } @@ -922,7 +1125,7 @@ static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; int space = CIRC_CNT(queue->tx_tail, queue->tx_head, - pdata->tx_ring_size); + pdata->tx_ring_size); return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; } @@ -1291,14 +1494,18 @@ static inline void phytmac_init_ring(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_queue *queue; unsigned int q = 0; + int i; for (queue = pdata->queues; q < pdata->queues_num; ++q) { queue->tx_head = 0; queue->tx_tail = 0; hw_if->clear_tx_desc(queue); + for (i = 0; i < pdata->rx_ring_size; i++) + hw_if->init_rx_map(queue, i); queue->rx_head = 0; queue->rx_tail = 0; + queue->rx_next_to_alloc = 0; phytmac_rx_clean(queue); ++queue; } @@ -1751,7 +1958,6 @@ static int phytmac_open(struct net_device *ndev) ++queue; } - phytmac_init_ring(pdata); hw_if->init_hw(pdata); ret = phytmac_phylink_connect(pdata); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index b823a07a731b67..9582b587342bb1 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -986,16 +986,6 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_FRAME_MASK; } -static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) -{ - dma_addr_t addr = 0; - - addr = ((u64)(desc->desc2) << 32); - - addr |= (desc->desc0 & 0xfffffffc); - return addr; -} - static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; @@ -1375,7 +1365,6 @@ struct phytmac_hw_if phytmac_1p0_hw = { .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, - .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, .rx_checksum = phytmac_rx_checksum, diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 41e5df41226e96..bcfc8ad68edc10 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -952,16 +952,6 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; } -static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) -{ - dma_addr_t addr = 0; - - addr = ((u64)(desc->desc2) << 32); - - addr |= (desc->desc0 & 0xfffffffc); - return addr; -} - static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; @@ -1229,7 +1219,6 @@ struct phytmac_hw_if phytmac_2p0_hw = { .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, - .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, .rx_checksum = phytmac_rx_checksum, From 0994f8fae927bbcd36998a124f9c290a872a90d1 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:10:12 +0800 Subject: [PATCH 319/521] PHYTIUM: net/phytmac: Round down rx_buf_len to 64 bytes DMA config register needs the rx_buf_len to be aligned to 64-bytes. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I7535ac7d8abaf80397b39ec1a16b1b959c9decfd Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/38380eb6dd3618217d0bc254e0d4b126b331c0c1 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index a2b3ed84e895a9..72b5a15870a08d 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -298,9 +298,9 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) static inline int phytmac_calc_rx_buf_len(void) { #if (PAGE_SIZE < 8192) - return PHYTMAC_MAX_FRAME_BUILD_SKB; + return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); #endif - return PHYTMAC_RXBUFFER_2048; + return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); } inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, From 98f98ea1ae457a751e4dc32dfd2b5e997cafaa56 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:11:35 +0800 Subject: [PATCH 320/521] PHYTIUM: net/phytmac: Add version display for phytmac driver Add support for Using the ethtool -i interface or modinfo phytmac/phytmac_platform command to display driver version information. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6577196db18b5b71252ed1e890bb997693460b2d Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/8f25123cc1d1a2ee58e4a61da2ba52e423a13ecf Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_ethtool.c | 16 ++++++++++++++++ drivers/net/ethernet/phytium/phytmac_main.c | 1 + drivers/net/ethernet/phytium/phytmac_pci.c | 1 + drivers/net/ethernet/phytium/phytmac_platform.c | 1 + 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 363bc65c72d474..7e6cf22d7b4f77 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" - +#define PHYTMAC_DRIVER_VERSION "1.0.0" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 7cddb01802f217..4f632591142fcc 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include "phytmac.h" #include "phytmac_v1.h" #include "phytmac_v2.h" @@ -502,6 +504,19 @@ static inline void phytmac_set_msglevel(struct net_device *ndev, u32 level) pdata->msg_enable = level; } +static void phytmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *drvinfo) +{ + struct phytmac *pdata = netdev_priv(ndev); + + strscpy(drvinfo->driver, PHYTMAC_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, PHYTMAC_DRIVER_VERSION, sizeof(drvinfo->version)); + + if (pdata->platdev) + strscpy(drvinfo->bus_info, pdata->platdev->name, sizeof(drvinfo->bus_info)); + else if (pdata->pcidev) + strscpy(drvinfo->bus_info, pci_name(pdata->pcidev), sizeof(drvinfo->bus_info)); +} + static const struct ethtool_ops phytmac_ethtool_ops = { .get_regs_len = phytmac_get_regs_len, .get_regs = phytmac_get_regs, @@ -524,6 +539,7 @@ static const struct ethtool_ops phytmac_ethtool_ops = { .set_channels = phytmac_set_channels, .get_wol = phytmac_get_wol, .set_wol = phytmac_set_wol, + .get_drvinfo = phytmac_get_drvinfo, }; void phytmac_set_ethtool_ops(struct net_device *ndev) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 72b5a15870a08d..79170097fd8300 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2488,4 +2488,5 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); MODULE_ALIAS("platform:phytmac"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index af69329fe06de5..60bd296d8f0b6c 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -316,3 +316,4 @@ module_pci_driver(phytmac_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium NIC PCI wrapper"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 305ff5866e2fe0..9390056fdc7a95 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -253,3 +253,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); MODULE_ALIAS("platform:phytmac"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); From 8e5945540bf6d0c62ddba624b5826e0f2002591b Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 29 Apr 2024 10:47:47 +0800 Subject: [PATCH 321/521] PHYTIUM: net/phytmac: Bugfix about dma conflict problem When setting RX_USED bit of the last descriptor for rx ring to 1, which will lead to the soft think it' valid and cause dma conflict. So change valid condition according to RX_USED bit and non-zero address. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I784537d0d55665587e01a186499416e48d46ad35 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/63d1449e25be6d4e0a3a6b9d0be14b59327e75fa Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 5 +++-- drivers/net/ethernet/phytium/phytmac_main.c | 19 ++++++++-------- drivers/net/ethernet/phytium/phytmac_v1.c | 25 ++++++++++++++++++--- drivers/net/ethernet/phytium/phytmac_v1.h | 1 - drivers/net/ethernet/phytium/phytmac_v2.c | 25 ++++++++++++++++++--- 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 7e6cf22d7b4f77..6eb1d7d1b8293b 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.0" +#define PHYTMAC_DRIVER_VERSION "1.0.1" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -509,13 +509,14 @@ struct phytmac_hw_if { void (*transmit)(struct phytmac_queue *queue); void (*restart)(struct phytmac *pdata); int (*tx_complete)(const struct phytmac_dma_desc *desc); - int (*rx_complete)(const struct phytmac_dma_desc *desc); + bool (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); bool (*rx_checksum)(const struct phytmac_dma_desc *desc); void (*set_desc_rxused)(struct phytmac_dma_desc *desc); bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); + unsigned int (*zero_rx_desc_addr)(struct phytmac_dma_desc *desc); void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); void (*clear_tx_desc)(struct phytmac_queue *queue); /* ptp */ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 79170097fd8300..d932d011b261fd 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -442,6 +442,7 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) int ret, i; tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_offset = ALIGN(tx_offset, 4096); tx_size = pdata->queues_num * tx_offset; for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { if (i == MAX_RING_ADDR_ALLOC_TIMES) @@ -504,6 +505,7 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) int ret, i; rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_offset = ALIGN(rx_offset, 4096); rx_size = pdata->queues_num * rx_offset; for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { if (i == MAX_RING_ADDR_ALLOC_TIMES) @@ -698,18 +700,13 @@ static bool phytmac_alloc_mapped_page(struct phytmac *pdata, return true; } -static inline bool phytmac_page_is_reserved(struct page *page) -{ - return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); -} - static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) { unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; struct page *page = rx_buffer->page; - /* avoid re-using remote pages */ - if (unlikely(phytmac_page_is_reserved(page))) + /* avoid re-using remote and pfmemalloc pages */ + if (!dev_page_is_reusable(page)) return false; #if (PAGE_SIZE < 8192) @@ -824,7 +821,6 @@ static void phytmac_add_rx_frag(struct phytmac_queue *queue, } static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, - struct phytmac_dma_desc *desc, unsigned int size) { struct sk_buff *skb; @@ -871,8 +867,9 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + hw_if->zero_rx_desc_addr(desc); - skb = phytmac_build_skb(rx_buffer, desc, len); + skb = phytmac_build_skb(rx_buffer, len); if (unlikely(!skb)) { netdev_err(pdata->ndev, "rx single build skb failed\n"); @@ -917,8 +914,9 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, desc = phytmac_get_rx_desc(queue, first_frag); rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); + hw_if->zero_rx_desc_addr(desc); - skb = phytmac_build_skb(rx_buffer, desc, frag_len); + skb = phytmac_build_skb(rx_buffer, frag_len); if (unlikely(!skb)) { netdev_err(pdata->ndev, "rx frame build skb failed\n"); pdata->ndev->stats.rx_dropped++; @@ -932,6 +930,7 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, for (frag = first_frag + 1; ; frag++) { desc = phytmac_get_rx_desc(queue, frag); rx_buffer = phytmac_get_rx_buffer(queue, frag, frag_len); + hw_if->zero_rx_desc_addr(desc); if (offset + frag_len > total_len) { if (unlikely(frag != last_frag)) { diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 9582b587342bb1..1d7c2e17591784 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -940,6 +940,14 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, return 0; } +static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = PHYTMAC_BIT(RX_USED); + + return 0; +} + static void phytmac_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; @@ -973,9 +981,19 @@ static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) return PHYTMAC_GET_BITS(desc->desc1, TX_USED); } -static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) { - return (desc->desc0 & PHYTMAC_BIT(RX_USED)) != 0; + dma_addr_t addr; + bool used; + + used = desc->desc0 & PHYTMAC_BIT(RX_USED); + addr = ((u64)(desc->desc2) << 32); + addr |= desc->desc0 & 0xfffffff8; + + if (used != 0 && addr != 0) + return true; + else + return false; } static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) @@ -1024,7 +1042,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en if (begin > end) tmp = end + queue->pdata->rx_ring_size; - for (frag = begin; frag != end; frag++) { + for (frag = begin; frag != tmp; frag++) { desc = phytmac_get_rx_desc(queue, frag); desc->desc0 &= ~PHYTMAC_BIT(RX_USED); } @@ -1373,6 +1391,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .rx_pkt_end = phytmac_rx_eof, .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, + .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, .set_time = phytmac_set_time, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 1f49d4ec79a04a..32bb1294910963 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -374,7 +374,6 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_recieve */ #define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 - #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index bcfc8ad68edc10..25f1ba6c1d6ef4 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -934,14 +934,32 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, return 0; } +static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = PHYTMAC_BIT(RXUSED); + + return 0; +} + static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) { return PHYTMAC_GET_BITS(desc->desc1, TXUSED); } -static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) { - return PHYTMAC_GET_BITS(desc->desc0, RXUSED); + dma_addr_t addr; + bool used; + + used = PHYTMAC_GET_BITS(desc->desc0, RXUSED); + addr = ((u64)(desc->desc2) << 32); + addr |= desc->desc0 & 0xfffffff8; + + if (used != 0 && addr != 0) + return true; + else + return false; } static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) @@ -990,7 +1008,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en if (begin > end) tmp = end + queue->pdata->rx_ring_size; - for (frag = begin; frag != end; frag++) { + for (frag = begin; frag != tmp; frag++) { desc = phytmac_get_rx_desc(queue, frag); desc->desc0 &= ~PHYTMAC_BIT(RXUSED); } @@ -1227,6 +1245,7 @@ struct phytmac_hw_if phytmac_2p0_hw = { .rx_pkt_end = phytmac_rx_eof, .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, + .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, From abb9781ecf508c7879259aaa34a4f25fcf49b5f9 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 29 Apr 2024 18:23:45 +0800 Subject: [PATCH 322/521] PHYTIUM: net/phytmac: Bugfix MAC address change when rmmod and insmod module The eth_hw_addr_random function cause systemd-udevd to generate MAC address, which will modify the default MAC address. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie9fa00c3a4c9310104fd888526571df95f15c664 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/6302b0ab85a13bec77c34afd9e0a015ac3b0ceda Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_main.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 6eb1d7d1b8293b..cb9359c7bd4dbb 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.1" +#define PHYTMAC_DRIVER_VERSION "1.0.2" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index d932d011b261fd..732358ea40afef 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2208,7 +2208,6 @@ static int phytmac_init(struct phytmac *pdata) ndev->netdev_ops = &phytmac_netdev_ops; phytmac_set_ethtool_ops(ndev); - eth_hw_addr_random(pdata->ndev); if (ndev->hw_features & NETIF_F_NTUPLE) { INIT_LIST_HEAD(&pdata->rx_fs_list.list); From a58096b4da071d5b9cd87a006bf3b7524ea21059 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 21 May 2024 14:20:05 +0800 Subject: [PATCH 323/521] PHYTIUM: net/phytmac: Bugfix MAC 10/100M speed does't work problem Gigabit_mode_enable bit should be clear when the speed is switched to 10M/100M. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I16e54d6e173c15c890788cf0bdcff2facca0f490 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/6b70dbeaafdc06a66410e77bfba11c5595f25a4e Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_ethtool.c | 2 +- drivers/net/ethernet/phytium/phytmac_v1.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index cb9359c7bd4dbb..bc0d2670c2f59a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.2" +#define PHYTMAC_DRIVER_VERSION "1.0.3" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 4f632591142fcc..a97a4c28c04fea 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -429,7 +429,7 @@ static int phytmac_set_link_ksettings(struct net_device *ndev, int ret = 0; if (!ndev->phydev) { - netdev_err(ndev, "fixed link interface not supported set link\n"); + netdev_err(ndev, "Without a PHY, setting link is not supported\n"); ret = -EOPNOTSUPP; } else { phy_ethtool_set_link_ksettings(ndev, kset); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 1d7c2e17591784..20595aca178343 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -182,7 +182,7 @@ static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); - config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD)); + config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); if (speed == SPEED_100) config |= PHYTMAC_BIT(SPEED); From 7923ce73fbd560d26bedbb906f471f4ee58d60f1 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 27 Jun 2024 10:09:53 +0800 Subject: [PATCH 324/521] PHYTIUM: net/phytmac: Adapt interface type 2500BASE-x Add 2500BASE-x interface type support for phytmac driver. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I9e0f81c8c9224a89be17714fb8a1b2905499f8bf Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/e1983bd12b88ce9b07660251c9d73d400cbab2e0 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_main.c | 10 +++++++--- drivers/net/ethernet/phytium/phytmac_v1.c | 21 +++++++++++++++------ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index bc0d2670c2f59a..a4e0d4e4fa1f35 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.3" +#define PHYTMAC_DRIVER_VERSION "1.0.4" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 732358ea40afef..43c15b8baf0b72 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1726,6 +1726,9 @@ static void phytmac_mac_link_up(struct phylink_config *config, pdata->pause = rx_pause; } + pdata->speed = speed; + pdata->duplex = duplex; + phytmac_init_ring(pdata); for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) @@ -1844,11 +1847,12 @@ static void phytmac_validate(struct phylink_config *config, if (state->interface == PHY_INTERFACE_MODE_5GBASER) phylink_set(mask, 5000baseT_Full); - if (state->interface == PHY_INTERFACE_MODE_1000BASEX || - state->interface == PHY_INTERFACE_MODE_SGMII || + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + phylink_set(mask, 1000baseX_Full); + + if (state->interface == PHY_INTERFACE_MODE_SGMII || phy_interface_mode_is_rgmii(state->interface)) { phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); phylink_set(mask, 1000baseT_Half); phylink_set(mask, 10baseT_Half); phylink_set(mask, 10baseT_Full); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 20595aca178343..72a6eeaec3563d 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1078,12 +1078,15 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); if (state->speed == SPEED_1000) config |= PHYTMAC_BIT(GM_EN); - else if (state->speed == SPEED_2500) - config |= PHYTMAC_BIT(2PT5G); + else if (state->speed == SPEED_2500) { + ctrl |= PHYTMAC_BIT(2PT5G); + config |= PHYTMAC_BIT(GM_EN); + } } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); } else if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { - config |= PHYTMAC_BIT(2PT5G) | PHYTMAC_BIT(PCS_EN); + ctrl |= PHYTMAC_BIT(2PT5G); + config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || state->interface == PHY_INTERFACE_MODE_USXGMII || state->interface == PHY_INTERFACE_MODE_5GBASER) { @@ -1111,11 +1114,17 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod if (old_config ^ config) PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); - /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ - if (state->interface == PHY_INTERFACE_MODE_SGMII) - phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); + /* Disable AN for SGMII fixed link or speed equal to 2.5G, enable otherwise.*/ + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + if (state->speed == SPEED_2500 || mode == MLO_AN_FIXED) + phytmac_enable_autoneg(pdata, 0); + else + phytmac_enable_autoneg(pdata, 1); + } if (state->interface == PHY_INTERFACE_MODE_1000BASEX) phytmac_enable_autoneg(pdata, 1); + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + phytmac_enable_autoneg(pdata, 0); } static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, From dd2384caeb7e248b0e001afa6160e7f35c6d8181 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 27 Jun 2024 10:15:06 +0800 Subject: [PATCH 325/521] PHYTIUM: net/phytmac: Support WOL interaction with BIOS It should notify the BIOS to enable or disable the WOL function. This patch adds support for it. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I1c900bc38faff9d0054c38329e55df8dd16e8d28 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/4c06f35e7d80ce9c350cfcfe8f35ec66326a12c0 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 3 +- .../net/ethernet/phytium/phytmac_ethtool.c | 1 + drivers/net/ethernet/phytium/phytmac_main.c | 34 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index a4e0d4e4fa1f35..df4ec49a085122 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.4" +#define PHYTMAC_DRIVER_VERSION "1.0.5" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -607,4 +607,5 @@ int phytmac_drv_resume(struct phytmac *pdata); struct phytmac *phytmac_alloc_pdata(struct device *dev); void phytmac_free_pdata(struct phytmac *pdata); int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); +void phytmac_set_bios_wol_enable(struct phytmac *pdata, u32 wol); #endif diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index a97a4c28c04fea..306a9934deabd7 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -131,6 +131,7 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) pdata->wol |= PHYTMAC_WAKE_MCAST; device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); return 0; } diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 43c15b8baf0b72..fd29c647e2775b 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -111,6 +111,8 @@ static int phytmac_set_mac_address(struct net_device *netdev, void *addr) hw_if->set_mac_address(pdata, saddr->sa_data); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); + return 0; } @@ -1971,6 +1973,7 @@ static int phytmac_open(struct net_device *ndev) } phylink_start(pdata->phylink); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); netif_tx_start_all_queues(pdata->ndev); @@ -2121,6 +2124,37 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, return features; } +void phytmac_set_bios_wol_enable(struct phytmac *pdata, u32 wol) +{ + struct net_device *ndev = pdata->ndev; + + if (ndev->phydev) { +#ifdef CONFIG_ACPI + if (has_acpi_companion(pdata->dev)) { + acpi_handle handle = ACPI_HANDLE(pdata->dev); + + if (acpi_has_method(handle, "PWOL")) { + union acpi_object args[] = { + { .type = ACPI_TYPE_INTEGER, }, + }; + struct acpi_object_list arg_input = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + + /* Set the input parameters */ + args[0].integer.value = wol; + + status = acpi_evaluate_object(handle, "PWOL", &arg_input, NULL); + if (ACPI_FAILURE(status)) + netdev_err(ndev, "The PWOL method failed to be executed.\n"); + } + } +#endif + } +} + int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) { int ret = 0; From 001d89e0576f9ecfa746cd7eb4f66744818b0a46 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Wed, 26 Feb 2025 03:50:13 +0000 Subject: [PATCH 326/521] PHYTIUM: net: phytmac: Manage WOL on MAC if PHY supports WOL Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/98c9f77e608579c85080570858a83b2eb8e3f307 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 306a9934deabd7..8bba0af005be88 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -116,7 +116,8 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) ret = phylink_ethtool_set_wol(pdata->phylink, wol); - if (!ret || ret != -EOPNOTSUPP) + /* Don't manage WoL on MAC, if PHY set_wol() fails */ + if (ret && ret != -EOPNOTSUPP) return ret; pdata->wol = 0; From a82b6e00586cf5f900d60ce41a62518ccad75abf Mon Sep 17 00:00:00 2001 From: zuoqian Date: Thu, 27 Feb 2025 05:44:58 +0000 Subject: [PATCH 327/521] PHYTIUM: net/phytmac: Bugfix set WOL failed issue Before configuring WOL, we need to obtain the packet types that WOL supports. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/88f00b76168d9ba6ee37c0286d5c750a4c6ab806 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 8bba0af005be88..e1698fa10b0955 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -89,23 +89,23 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); + wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); + wol->supported = WAKE_MAGIC | WAKE_ARP | + WAKE_UCAST | WAKE_MCAST; + if (pdata->wol & PHYTMAC_WAKE_MAGIC) { wol->wolopts |= WAKE_MAGIC; - wol->supported |= WAKE_MAGIC; } if (pdata->wol & PHYTMAC_WAKE_ARP) { wol->wolopts |= WAKE_ARP; - wol->supported |= WAKE_ARP; } if (pdata->wol & PHYTMAC_WAKE_UCAST) { wol->wolopts |= WAKE_UCAST; - wol->supported |= WAKE_UCAST; } if (pdata->wol & PHYTMAC_WAKE_MCAST) { wol->wolopts |= WAKE_MCAST; - wol->supported |= WAKE_MCAST; } } From 1d599e1f73b93458fa8d4a65be82438e886d1a63 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 15 Aug 2024 17:06:59 +0800 Subject: [PATCH 328/521] PHYTIUM: net/phytmac: Add support for phytmac v2.0 This patch Adds support for phtymac v2.0,including acpi description, rx tail pointer supporting, power management and other functions. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I4758cfb98004683e6ea79079ec263fbc49e46c69 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/3f2b924070a4f3937eef439656d0971eaaa2aa14 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/Makefile | 2 +- drivers/net/ethernet/phytium/phytmac.h | 57 +- .../net/ethernet/phytium/phytmac_ethtool.c | 1 + drivers/net/ethernet/phytium/phytmac_main.c | 104 ++- drivers/net/ethernet/phytium/phytmac_pci.c | 4 +- .../net/ethernet/phytium/phytmac_platform.c | 147 ++++- drivers/net/ethernet/phytium/phytmac_ptp.c | 3 + drivers/net/ethernet/phytium/phytmac_ptp.h | 1 + drivers/net/ethernet/phytium/phytmac_v1.c | 118 +++- drivers/net/ethernet/phytium/phytmac_v1.h | 19 +- drivers/net/ethernet/phytium/phytmac_v2.c | 606 +++++++++++------- drivers/net/ethernet/phytium/phytmac_v2.h | 74 ++- 12 files changed, 796 insertions(+), 340 deletions(-) diff --git a/drivers/net/ethernet/phytium/Makefile b/drivers/net/ethernet/phytium/Makefile index 6e710d4d54b664..debaed63e45388 100644 --- a/drivers/net/ethernet/phytium/Makefile +++ b/drivers/net/ethernet/phytium/Makefile @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. # # Makefile for the Phytium network device drivers. # -# obj-$(CONFIG_PHYTMAC) += phytmac.o diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index df4ec49a085122..3806e09bc1220a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_H #define _PHYTMAC_H @@ -13,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.5" +#define PHYTMAC_DRIVER_VERSION "1.0.29" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -44,6 +46,8 @@ #define PHYTMAC_POWEROFF 1 #define PHYTMAC_POWERON 2 +#define PHYTMAC_PWCTL_GMAC_ID 6 +#define PHYTMAC_PWCTL_DEFAULT_VAL 0 #define PHYTMAC_WOL_MAGIC_PACKET 1 @@ -58,8 +62,12 @@ #define PHYTMAC_CAPS_TAILPTR 0x00000040 #define PHYTMAC_CAPS_START 0x00000080 #define PHYTMAC_CAPS_NO_WOL 0x0000100 -#define PHYTMAC_CAPS_LPI 0x0000400 +#define PHYTMAC_CAPS_PWCTRL 0x0000200 #define PHYTMAC_CAPS_MSG 0x0000800 +#define PHYTMAC_CAPS_RXPTR 0x0001000 + +#define VERSION_V0 0 +#define VERSION_V3 0x3 #define PHYTMAC_TX 0x1 #define PHYTMAC_RX 0x2 @@ -122,6 +130,39 @@ #define PHYTMAC_WAKE_UCAST 0x00000004 #define PHYTMAC_WAKE_MCAST 0x00000008 +enum phytmac_interface { + PHYTMAC_PHY_INTERFACE_MODE_NA, + PHYTMAC_PHY_INTERFACE_MODE_INTERNAL, + PHYTMAC_PHY_INTERFACE_MODE_MII, + PHYTMAC_PHY_INTERFACE_MODE_GMII, + PHYTMAC_PHY_INTERFACE_MODE_SGMII, + PHYTMAC_PHY_INTERFACE_MODE_TBI, + PHYTMAC_PHY_INTERFACE_MODE_REVMII, + PHYTMAC_PHY_INTERFACE_MODE_RMII, + PHYTMAC_PHY_INTERFACE_MODE_RGMII, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_ID, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_RXID, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_TXID, + PHYTMAC_PHY_INTERFACE_MODE_RTBI, + PHYTMAC_PHY_INTERFACE_MODE_SMII, + PHYTMAC_PHY_INTERFACE_MODE_XGMII, + PHYTMAC_PHY_INTERFACE_MODE_MOCA, + PHYTMAC_PHY_INTERFACE_MODE_QSGMII, + PHYTMAC_PHY_INTERFACE_MODE_TRGMII, + PHYTMAC_PHY_INTERFACE_MODE_100BASEX, + PHYTMAC_PHY_INTERFACE_MODE_1000BASEX, + PHYTMAC_PHY_INTERFACE_MODE_2500BASEX, + PHYTMAC_PHY_INTERFACE_MODE_5GBASER, + PHYTMAC_PHY_INTERFACE_MODE_RXAUI, + PHYTMAC_PHY_INTERFACE_MODE_XAUI, + /* 10GBASE-R, XFI, SFI - single lane 10G Serdes */ + PHYTMAC_PHY_INTERFACE_MODE_10GBASER, + PHYTMAC_PHY_INTERFACE_MODE_USXGMII, + /* 10GBASE-KR - with Clause 73 AN */ + PHYTMAC_PHY_INTERFACE_MODE_10GKR, + PHYTMAC_PHY_INTERFACE_MODE_MAX, +}; + struct packet_info { int lso; int desc_cnt; @@ -214,7 +255,6 @@ static const struct phytmac_statistics queue_statistics[] = { struct phytmac_config { struct phytmac_hw_if *hw_if; u32 caps; - u32 tsu_rate; u16 queue_num; }; @@ -367,8 +407,8 @@ struct phytmac_msg { u32 tx_msg_tail; u32 rx_msg_head; u32 rx_msg_tail; - /* Lock to protect msg */ - spinlock_t msg_lock; + /*use msg_mutex to protect msg */ + struct mutex msg_mutex; }; struct ts_ctrl { @@ -401,7 +441,6 @@ struct phytmac { u32 min_tx_length; u32 jumbo_len; u32 wol; - u32 lpi; u32 power_state; struct work_struct restart_task; /* Lock to protect mac config */ @@ -430,6 +469,7 @@ struct phytmac { struct phylink_pcs phylink_pcs; int pause; phy_interface_t phy_interface; + enum phytmac_interface phytmac_v2_interface; int speed; int duplex; int autoneg; @@ -446,6 +486,7 @@ struct phytmac { /* Lock to protect fs */ spinlock_t rx_fs_lock; unsigned int max_rx_fs; + u32 version; }; struct phytmac_hw_if { @@ -507,7 +548,7 @@ struct phytmac_hw_if { void (*init_rx_map)(struct phytmac_queue *queue, u32 index); unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); void (*transmit)(struct phytmac_queue *queue); - void (*restart)(struct phytmac *pdata); + void (*update_rx_tail)(struct phytmac_queue *queue); int (*tx_complete)(const struct phytmac_dma_desc *desc); bool (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); @@ -517,6 +558,7 @@ struct phytmac_hw_if { bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); unsigned int (*zero_rx_desc_addr)(struct phytmac_dma_desc *desc); + unsigned int (*zero_tx_desc)(struct phytmac_dma_desc *desc); void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); void (*clear_tx_desc)(struct phytmac_queue *queue); /* ptp */ @@ -604,6 +646,7 @@ int phytmac_drv_probe(struct phytmac *pdata); int phytmac_drv_remove(struct phytmac *pdata); int phytmac_drv_suspend(struct phytmac *pdata); int phytmac_drv_resume(struct phytmac *pdata); +void phytmac_drv_shutdown(struct phytmac *pdata); struct phytmac *phytmac_alloc_pdata(struct device *dev); void phytmac_free_pdata(struct phytmac *pdata); int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index e1698fa10b0955..e05274b961e4b1 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ #include #include diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index fd29c647e2775b..dcede058512c65 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2,6 +2,8 @@ /* * Phytium Ethernet Controller driver * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * * 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. @@ -125,10 +127,12 @@ static int phytmac_get_mac_address(struct phytmac *pdata) if (is_valid_ether_addr(addr)) { eth_hw_addr_set(pdata->ndev, addr); + ether_addr_copy(pdata->ndev->perm_addr, addr); return 0; } dev_info(pdata->dev, "invalid hw address, using random\n"); eth_hw_addr_random(pdata->ndev); + ether_addr_copy(pdata->ndev->perm_addr, pdata->ndev->dev_addr); return 0; } @@ -1033,6 +1037,8 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) queue->rx_next_to_alloc = queue->rx_head; /* make newly descriptor to hardware */ wmb(); + + hw_if->update_rx_tail(queue); } static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, @@ -1143,7 +1149,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) unsigned int tail = queue->tx_tail; unsigned int head; - spin_lock(&pdata->lock); + spin_lock(&queue->tx_lock); for (head = queue->tx_head; head != tail && packet_count < budget; ) { struct sk_buff *skb; @@ -1180,7 +1186,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) packet_count++; } - /* Now we can safely release resources */ + /* Now we can safely release resources */ phytmac_tx_unmap(pdata, tx_skb, budget); if (complete) { @@ -1198,7 +1204,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) if (__netif_subqueue_stopped(pdata->ndev, queue_index) && (phytmac_maybe_wake_tx_queue(queue))) netif_wake_subqueue(pdata->ndev, queue_index); - spin_unlock(&pdata->lock); + spin_unlock(&queue->tx_lock); return packet_count; } @@ -1281,6 +1287,7 @@ static inline int phytmac_clear_csum(struct sk_buff *skb) static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) { +#ifdef PHYTMAC_SW_FCS bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) || skb_is_nonlinear(*skb); int padlen = ETH_ZLEN - (*skb)->len; @@ -1290,7 +1297,7 @@ static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) u32 fcs; int i; - if ((ndev->features & NETIF_F_HW_CSUM) || + if (!(ndev->features & NETIF_F_HW_CSUM) || !((*skb)->ip_summed != CHECKSUM_PARTIAL) || skb_shinfo(*skb)->gso_size || phytmac_ptp_one_step(*skb)) return 0; @@ -1327,6 +1334,8 @@ static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) for (i = 0; i < 4; ++i) skb_put_u8(*skb, (fcs >> (i * 8)) & 0xff); +#endif + return 0; } @@ -1376,13 +1385,13 @@ static int phytmac_packet_info(struct phytmac *pdata, desc_cnt += TXD_USE_COUNT(pdata, skb_frag_size(&skb_shinfo(skb)->frags[f])); packet->desc_cnt = desc_cnt; - if ((!(pdata->ndev->features & NETIF_F_HW_CSUM)) && +#ifdef PHYTMAC_SW_FCS + if ((pdata->ndev->features & NETIF_F_HW_CSUM) && skb->ip_summed != CHECKSUM_PARTIAL && !is_lso && !phytmac_ptp_one_step(skb)) packet->nocrc = 1; - else - packet->nocrc = 0; +#endif if (netif_msg_pktdata(pdata)) { netdev_info(pdata->ndev, "packet info: desc_cnt=%d, nocrc=%d,ip_summed=%d\n", @@ -1522,7 +1531,6 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd struct phytmac_queue *queue = &pdata->queues[queue_index]; netdev_tx_t ret = NETDEV_TX_OK; struct packet_info packet; - unsigned long flags; if (phytmac_clear_csum(skb)) { dev_kfree_skb_any(skb); @@ -1543,7 +1551,7 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd if (netif_msg_pktdata(pdata)) phytmac_dump_pkt(pdata, skb, true); - spin_lock_irqsave(&pdata->lock, flags); + spin_lock_bh(&queue->tx_lock); /* Check that there are enough descriptors available */ ret = phytmac_maybe_stop_tx_queue(queue, packet.desc_cnt); if (ret) @@ -1562,7 +1570,7 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd hw_if->transmit(queue); tx_return: - spin_unlock_irqrestore(&pdata->lock, flags); + spin_unlock_bh(&queue->tx_lock); return ret; } @@ -1668,6 +1676,7 @@ static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mo unsigned int q; unsigned long flags; struct phytmac_tx_skb *tx_skb; + struct phytmac_dma_desc *tx_desc = NULL; int i; if (netif_msg_link(pdata)) { @@ -1686,18 +1695,22 @@ static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mo /* Disable Rx and Tx */ hw_if->enable_network(pdata, false, PHYTMAC_RX | PHYTMAC_TX); + spin_unlock_irqrestore(&pdata->lock, flags); /* Tx clean */ for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + spin_lock_bh(&queue->tx_lock); for (i = 0; i < pdata->tx_ring_size; i++) { tx_skb = phytmac_get_tx_skb(queue, i); if (tx_skb) phytmac_tx_unmap(pdata, tx_skb, 0); + + tx_desc = phytmac_get_tx_desc(queue, i); + hw_if->zero_tx_desc(tx_desc); } + spin_unlock_bh(&queue->tx_lock); } - spin_unlock_irqrestore(&pdata->lock, flags); - netif_tx_stop_all_queues(ndev); } @@ -1890,6 +1903,8 @@ static int phytmac_phylink_create(struct phytmac *pdata) pdata->phylink_config.dev = &pdata->ndev->dev; pdata->phylink_config.type = PHYLINK_NETDEV; + pdata->phylink_config.mac_managed_pm = true; + if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII || pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX || pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX || @@ -1923,10 +1938,6 @@ static int phytmac_open(struct net_device *ndev) if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "open\n"); - /* phytmac_powerup */ - if (pdata->power_state == PHYTMAC_POWEROFF) - hw_if->poweron(pdata, PHYTMAC_POWERON); - if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); @@ -2033,10 +2044,6 @@ static int phytmac_close(struct net_device *ndev) if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) phytmac_ptp_unregister(pdata); - /* phytmac_powerup */ - if (pdata->power_state == PHYTMAC_POWERON) - hw_if->poweron(pdata, PHYTMAC_POWEROFF); - return 0; } @@ -2309,6 +2316,7 @@ int phytmac_drv_probe(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; struct device *dev = pdata->dev; + struct phytmac_hw_if *hw_if = pdata->hw_if; int ret = 0; if (netif_msg_probe(pdata)) @@ -2332,12 +2340,13 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_out; } - netif_carrier_off(ndev); - ret = register_netdev(ndev); - if (ret) { - dev_err(pdata->dev, "Cannot register net device, aborting.\n"); - goto err_out; - } + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + mutex_init(&pdata->msg_ring.msg_mutex); if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); @@ -2359,6 +2368,13 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + ret = register_netdev(ndev); + if (ret) { + dev_err(pdata->dev, "Cannot register net device, aborting.\n"); + goto err_phylink_init; + } + netif_carrier_off(ndev); + if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); @@ -2373,8 +2389,6 @@ int phytmac_drv_probe(struct phytmac *pdata) if (pdata->mii_bus) mdiobus_free(pdata->mii_bus); - unregister_netdev(ndev); - err_out: return ret; } @@ -2383,12 +2397,15 @@ EXPORT_SYMBOL_GPL(phytmac_drv_probe); int phytmac_drv_remove(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; + struct phytmac_hw_if *hw_if = pdata->hw_if; if (ndev) { if (pdata->use_ncsi && pdata->ncsidev) ncsi_unregister_dev(pdata->ncsidev); unregister_netdev(ndev); + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); if (pdata->use_mii && pdata->mii_bus) { mdiobus_unregister(pdata->mii_bus); @@ -2397,6 +2414,8 @@ int phytmac_drv_remove(struct phytmac *pdata) if (pdata->phylink) phylink_destroy(pdata->phylink); + + mutex_destroy(&pdata->msg_ring.msg_mutex); } return 0; @@ -2422,12 +2441,15 @@ int phytmac_drv_suspend(struct phytmac *pdata) /* napi_disable */ for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + hw_if->clear_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); napi_disable(&queue->tx_napi); napi_disable(&queue->rx_napi); } if (pdata->wol) { hw_if->set_wol(pdata, pdata->wol); + pdata->power_state = PHYTMAC_POWEROFF; } else { rtnl_lock(); phylink_stop(pdata->phylink); @@ -2449,15 +2471,14 @@ int phytmac_drv_resume(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct ethtool_rx_fs_item *item; - if (!netif_running(pdata->ndev)) - return 0; - - if (pdata->power_state == PHYTMAC_POWEROFF) - hw_if->poweron(pdata, PHYTMAC_POWERON); + hw_if->poweron(pdata, PHYTMAC_POWERON); if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); + if (!netif_running(pdata->ndev)) + return 0; + if (pdata->wol) { hw_if->set_wol(pdata, 0); rtnl_lock(); @@ -2520,6 +2541,23 @@ void phytmac_free_pdata(struct phytmac *pdata) } EXPORT_SYMBOL_GPL(phytmac_free_pdata); +void phytmac_drv_shutdown(struct phytmac *pdata) +{ + struct net_device *netdev = pdata->ndev; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + rtnl_lock(); + netif_device_detach(netdev); + + if (netif_running(netdev)) + phytmac_close(netdev); + rtnl_unlock(); + + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); +} +EXPORT_SYMBOL_GPL(phytmac_drv_shutdown); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index 60bd296d8f0b6c..62766c49d1a626 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -2,6 +2,9 @@ /* * Phytium GMAC PCI wrapper. * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #include @@ -21,7 +24,6 @@ struct phytmac_data { struct phytmac_hw_if *hw_if; u32 caps; - u32 tsu_rate; u16 queue_num; int speed; bool duplex; diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 9390056fdc7a95..95509711894b05 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -2,6 +2,9 @@ /* * Phytium GMAC Platform wrapper. * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -21,18 +24,17 @@ static const struct phytmac_config phytium_1p0_config = { | PHYTMAC_CAPS_JUMBO | PHYTMAC_CAPS_LSO, .queue_num = 4, - .tsu_rate = 300000000, }; static const struct phytmac_config phytium_2p0_config = { .hw_if = &phytmac_2p0_hw, .caps = PHYTMAC_CAPS_TAILPTR - | PHYTMAC_CAPS_LPI + | PHYTMAC_CAPS_RXPTR + | PHYTMAC_CAPS_PWCTRL | PHYTMAC_CAPS_LSO | PHYTMAC_CAPS_MSG | PHYTMAC_CAPS_JUMBO, .queue_num = 2, - .tsu_rate = 300000000, }; #if defined(CONFIG_OF) @@ -47,7 +49,8 @@ MODULE_DEVICE_TABLE(of, phytmac_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id phytmac_acpi_ids[] = { { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_1p0_config }, - { } + { .id = "PHYT0056", .driver_data = (kernel_ulong_t)&phytium_2p0_config }, + {} }; MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); @@ -55,6 +58,95 @@ MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); #define phytmac_acpi_ids NULL #endif +static const char *phytmac_phy_modes(enum phytmac_interface interface) +{ + switch (interface) { + case PHYTMAC_PHY_INTERFACE_MODE_NA: + return ""; + case PHYTMAC_PHY_INTERFACE_MODE_INTERNAL: + return "internal"; + case PHYTMAC_PHY_INTERFACE_MODE_MII: + return "mii"; + case PHYTMAC_PHY_INTERFACE_MODE_GMII: + return "gmii"; + case PHYTMAC_PHY_INTERFACE_MODE_SGMII: + return "sgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_TBI: + return "tbi"; + case PHYTMAC_PHY_INTERFACE_MODE_REVMII: + return "rev-mii"; + case PHYTMAC_PHY_INTERFACE_MODE_RMII: + return "rmii"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII: + return "rgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_ID: + return "rgmii-id"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_RXID: + return "rgmii-rxid"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_TXID: + return "rgmii-txid"; + case PHYTMAC_PHY_INTERFACE_MODE_RTBI: + return "rtbi"; + case PHYTMAC_PHY_INTERFACE_MODE_SMII: + return "smii"; + case PHYTMAC_PHY_INTERFACE_MODE_XGMII: + return "xgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_MOCA: + return "moca"; + case PHYTMAC_PHY_INTERFACE_MODE_QSGMII: + return "qsgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_TRGMII: + return "trgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_100BASEX: + return "100base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_1000BASEX: + return "1000base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_2500BASEX: + return "2500base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_5GBASER: + return "5gbase-r"; + case PHYTMAC_PHY_INTERFACE_MODE_RXAUI: + return "rxaui"; + case PHYTMAC_PHY_INTERFACE_MODE_XAUI: + return "xaui"; + case PHYTMAC_PHY_INTERFACE_MODE_10GBASER: + return "10gbase-r"; + case PHYTMAC_PHY_INTERFACE_MODE_USXGMII: + return "usxgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_10GKR: + return "10gbase-kr"; + default: + return "unknown"; + } +} + +static int phytmac_v2_get_phy_mode(struct platform_device *pdev) +{ + const char *pm; + int err, i; + int phy_interface; + + err = device_property_read_string(&pdev->dev, "phy-mode", &pm); + if (err < 0) + return err; + + phy_interface = PHYTMAC_PHY_INTERFACE_MODE_MAX + 1; + for (i = 0; i < PHYTMAC_PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phytmac_phy_modes(i))) { + phy_interface = i; + dev_notice(&pdev->dev, "Phy mode is %s.\n", pm); + break; + } + } + + if (phy_interface > PHYTMAC_PHY_INTERFACE_MODE_MAX) { + dev_err(&pdev->dev, "Invalid phy mode value: %s!\n", pm); + return -EINVAL; + } + + return phy_interface; +} + static int phytmac_get_phy_mode(struct platform_device *pdev) { const char *pm; @@ -80,6 +172,8 @@ static int phytmac_plat_probe(struct platform_device *pdev) struct phytmac *pdata; int ret, i; u32 queue_num; + const struct of_device_id *match = NULL; + const struct acpi_device_id *match_acpi = NULL; pdata = phytmac_alloc_pdata(&pdev->dev); if (IS_ERR(pdata)) { @@ -92,8 +186,6 @@ static int phytmac_plat_probe(struct platform_device *pdev) pdata->platdev = pdev; if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(phytmac_dt_ids, np); if (match && match->data) { phytmac_config = match->data; @@ -102,11 +194,9 @@ static int phytmac_plat_probe(struct platform_device *pdev) pdata->queues_max_num = phytmac_config->queue_num; } } else if (has_acpi_companion(&pdev->dev)) { - const struct acpi_device_id *match; - - match = acpi_match_device(phytmac_acpi_ids, &pdev->dev); - if (match && match->driver_data) { - phytmac_config = (void *)match->driver_data; + match_acpi = acpi_match_device(phytmac_acpi_ids, &pdev->dev); + if (match_acpi && match_acpi->driver_data) { + phytmac_config = (void *)match_acpi->driver_data; pdata->hw_if = phytmac_config->hw_if; pdata->capacities = phytmac_config->caps; pdata->queues_max_num = phytmac_config->queue_num; @@ -122,6 +212,18 @@ static int phytmac_plat_probe(struct platform_device *pdev) } pdata->ndev->base_addr = regs->start; + if (pdev->dev.of_node && match) { + if (!strcmp(match->compatible, "phytium,gmac-1.0")) + pdata->version = PHYTMAC_READ(pdata, PHYTMAC_VERSION) & 0xff; + else + pdata->version = VERSION_V3; + } else if (has_acpi_companion(&pdev->dev) && match_acpi) { + if (!strcmp(match_acpi->id, "PHYT0046")) + pdata->version = PHYTMAC_READ(pdata, PHYTMAC_VERSION) & 0xff; + else + pdata->version = VERSION_V3; + } + if (pdata->capacities & PHYTMAC_CAPS_MSG) { ++i; regs = platform_get_resource(pdev, IORESOURCE_MEM, i); @@ -134,11 +236,10 @@ static int phytmac_plat_probe(struct platform_device *pdev) } } - if (device_property_read_bool(&pdev->dev, "lpi")) - pdata->capacities |= PHYTMAC_CAPS_LPI; + if (device_property_read_bool(&pdev->dev, "powerctrl")) + pdata->capacities |= PHYTMAC_CAPS_PWCTRL; - if (pdata->capacities & PHYTMAC_CAPS_LPI) { - /* lpi resource */ + if (pdata->version == VERSION_V3 && pdev->dev.of_node) { ++i; regs = platform_get_resource(pdev, IORESOURCE_MEM, i); if (regs) { @@ -181,6 +282,14 @@ static int phytmac_plat_probe(struct platform_device *pdev) else pdata->phy_interface = ret; + if (pdata->version == VERSION_V3) { + ret = phytmac_v2_get_phy_mode(pdev); + if (ret < 0) + pdata->phytmac_v2_interface = PHYTMAC_PHY_INTERFACE_MODE_USXGMII; + else + pdata->phytmac_v2_interface = ret; + } + ret = phytmac_drv_probe(pdata); if (ret) goto err_mem; @@ -212,6 +321,13 @@ static int phytmac_plat_remove(struct platform_device *pdev) return 0; } +static void phytmac_plat_shutdown(struct platform_device *pdev) +{ + struct phytmac *pdata = platform_get_drvdata(pdev); + + phytmac_drv_shutdown(pdata); +} + static int __maybe_unused phytmac_plat_suspend(struct device *dev) { struct phytmac *pdata = dev_get_drvdata(dev); @@ -245,6 +361,7 @@ static struct platform_driver phytmac_driver = { .acpi_match_table = phytmac_acpi_ids, .pm = &phytmac_plat_pm_ops, }, + .shutdown = phytmac_plat_shutdown, }; module_platform_driver(phytmac_driver); diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.c b/drivers/net/ethernet/phytium/phytmac_ptp.c index 26b1b75edbde1d..4803842bd7c17c 100644 --- a/drivers/net/ethernet/phytium/phytmac_ptp.c +++ b/drivers/net/ethernet/phytium/phytmac_ptp.c @@ -2,6 +2,9 @@ /** * 1588 PTP support for Phytium GMAC device. * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #include #include diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.h b/drivers/net/ethernet/phytium/phytmac_ptp.h index 72c8b7c6741370..e9497a172d25e2 100644 --- a/drivers/net/ethernet/phytium/phytmac_ptp.h +++ b/drivers/net/ethernet/phytium/phytmac_ptp.h @@ -2,6 +2,7 @@ /* * Phytium Ethernet Controller driver * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. * * 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 diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 72a6eeaec3563d..4fc0bfa2f696ec 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_v1.h" @@ -211,6 +213,11 @@ static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, else PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + if (interface == PHY_INTERFACE_MODE_SGMII) { + if (speed == SPEED_2500) + phytmac_enable_autoneg(pdata, 0); + } + return 0; } @@ -289,7 +296,6 @@ static int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) static void phytmac_reset_hw(struct phytmac *pdata) { - struct phytmac_queue *queue; unsigned int q; u32 ctrl; @@ -300,7 +306,7 @@ static void phytmac_reset_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); /* Disable and clear all interrupts and disable queues */ - for (q = 0, queue = pdata->queues; q < pdata->queues_max_num; ++q, ++queue) { + for (q = 0; q < pdata->queues_max_num; ++q) { if (q == 0) { PHYTMAC_WRITE(pdata, PHYTMAC_ID, -1); PHYTMAC_WRITE(pdata, PHYTMAC_IS, -1); @@ -317,7 +323,10 @@ static void phytmac_reset_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), 0); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(q), 0); + + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_TAILPTR(q), 0); } } @@ -352,6 +361,7 @@ static int phytmac_init_hw(struct phytmac *pdata) u32 config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); u32 dmaconfig; u32 nctrlconfig; + u32 ptrconfig = 0; nctrlconfig = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); nctrlconfig |= PHYTMAC_BIT(MPE); @@ -414,7 +424,14 @@ static int phytmac_init_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, PHYTMAC_BIT(TXTAIL_ENABLE)); + ptrconfig |= PHYTMAC_BIT(TXPTR_EN); + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + ptrconfig |= PHYTMAC_BIT(RXPTR_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, ptrconfig); + + if (pdata->capacities & PHYTMAC_CAPS_START) + PHYTMAC_WRITE(pdata, PHYTMAC_TXSTARTSEL, PHYTMAC_BIT(TSTARTSEL_ENABLE)); if (phy_interface_mode_is_8023z(pdata->phy_interface)) phytmac_pcs_software_reset(pdata, 1); @@ -426,8 +443,46 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) { u32 status, data0, data1, rdata1; int ret; + acpi_handle handle; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status acpi_sts; + unsigned long long rv; + + if (!(pdata->capacities & PHYTMAC_CAPS_PWCTRL)) { + pdata->power_state = on; + return 0; + } + + if (has_acpi_companion(pdata->dev)) { + handle = ACPI_HANDLE(pdata->dev); - if (pdata->capacities & PHYTMAC_CAPS_LPI) { + netdev_info(pdata->ndev, "set gmac power %s\n", + on == PHYTMAC_POWERON ? "on" : "off"); + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + + if (on == PHYTMAC_POWERON) { + acpi_sts = acpi_evaluate_integer(handle, "PPWO", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWO Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power on\n"); + } else { + acpi_sts = acpi_evaluate_integer(handle, "PPWD", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWD Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power off\n"); + } + } else { ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, 1, PHYTMAC_TIMEOUT); if (ret) @@ -460,16 +515,17 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) data0 & PHYTMAC_BIT(DATA0_FREE), 1, PHYTMAC_TIMEOUT); if (ret) - netdev_err(pdata->ndev, "mnh data0 is busy"); + netdev_err(pdata->ndev, "mnh data0 is busy\n"); rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } + pdata->power_state = on; return 0; @@ -626,6 +682,9 @@ static int phytmac_get_feature_all(struct phytmac *pdata) /* max rx fs */ pdata->max_rx_fs = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT3, SCR2CMP); + if (pdata->version == VERSION_V3) + pdata->capacities |= PHYTMAC_CAPS_RXPTR; + if (netif_msg_hw(pdata)) netdev_info(pdata->ndev, "get feature queue_num=%d, daw=%d, dbw=%d, rx_fs=%d, rx_bd=%d, tx_bd=%d\n", pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, @@ -756,7 +815,7 @@ static int phytmac_init_ring_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), upper_32_bits(queue->rx_ring_addr)); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(q), queue->tx_tail); } return 0; @@ -928,7 +987,8 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); /* Make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 = lower_32_bits(addr); } else { desc->desc1 = 0; @@ -948,32 +1008,33 @@ static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) return 0; } +static unsigned int phytmac_zero_tx_desc(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = 0; + desc->desc1 &= ~PHYTMAC_BIT(TX_USED); + + return 0; +} + static void phytmac_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(queue->index), PHYTMAC_BIT(TXTAIL_UPDATE) | queue->tx_tail); - if (pdata->capacities & PHYTMAC_CAPS_START) - PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, - PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); + if (likely(pdata->capacities & PHYTMAC_CAPS_START)) + PHYTMAC_WRITE(pdata, PHYTMAC_TXSTART, PHYTMAC_BIT(TSTART)); } -static void phytmac_restart(struct phytmac *pdata) +static void phytmac_update_rx_tail(struct phytmac_queue *queue) { - int q; - struct phytmac_queue *queue; + struct phytmac *pdata = queue->pdata; - for (q = 0; q < pdata->queues_num; q++) { - queue = &pdata->queues[q]; - if (queue->tx_head != queue->tx_tail) { - PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, - PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); - break; - } - } + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_TAILPTR(queue->index), queue->rx_head); } static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) @@ -1116,7 +1177,7 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod /* Disable AN for SGMII fixed link or speed equal to 2.5G, enable otherwise.*/ if (state->interface == PHY_INTERFACE_MODE_SGMII) { - if (state->speed == SPEED_2500 || mode == MLO_AN_FIXED) + if (mode == MLO_AN_FIXED) phytmac_enable_autoneg(pdata, 0); else phytmac_enable_autoneg(pdata, 1); @@ -1158,7 +1219,7 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) desc->desc1 |= PHYTMAC_BIT(TX_WRAP); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(queue->index), queue->tx_tail); } static void phytmac_get_hw_stats(struct phytmac *pdata) @@ -1388,7 +1449,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { /* tx and rx */ .tx_map = phytmac_tx_map_desc, .transmit = phytmac_tx_start, - .restart = phytmac_restart, + .update_rx_tail = phytmac_update_rx_tail, .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, @@ -1401,6 +1462,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, + .zero_tx_desc = phytmac_zero_tx_desc, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, .set_time = phytmac_set_time, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 32bb1294910963..e0ca0ab8354749 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_V1_H #define _PHYTMAC_V1_H @@ -53,6 +55,9 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_USXSTATUS 0x0A88 #define PHYTMAC_TXBDCTRL 0x04CC #define PHYTMAC_RXBDCTRL 0x04D0 +#define PHYTMAC_TXSTART 0x0E70 +#define PHYTMAC_TXSTARTSEL 0x0E74 +#define PHYTMAC_VERSION 0x0FF0 /* Fdir match registers */ #define PHYTMAC_FDIR(i) (0x0540 + ((i) << 2)) @@ -75,7 +80,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_IDR(i) (0x0620 + ((i) << 2)) #define PHYTMAC_IMR(i) (0x0640 + ((i) << 2)) #define PHYTMAC_TAIL_ENABLE (0x0e7c) -#define PHYTMAC_TAILPTR(i) (0x0e80 + ((i) << 2)) +#define PHYTMAC_TX_TAILPTR(i) (0x0e80 + ((i) << 2)) +#define PHYTMAC_RX_TAILPTR(i) (0x0ec0 + ((i) << 2)) #define PHYTMAC_PHY_INT_ENABLE 0x1C88 #define PHYTMAC_PHY_INT_CLEAR 0x1C8C @@ -363,8 +369,11 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_TXTAIL_UPDATE_WIDTH 1 /* Bitfields in TAIL_ENABLE */ -#define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ -#define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 +#define PHYTMAC_TXPTR_EN_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXPTR_EN_WIDTH 1 + +#define PHYTMAC_RXPTR_EN_INDEX 16 /* Enable rx tail */ +#define PHYTMAC_RXPTR_EN_WIDTH 1 /* Bitfields in INT ENABLE */ #define PHYTMAC_WOL_RECEIVE_ENABLE_INDEX 28 /* Enable wol_event_recieve */ @@ -374,6 +383,10 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_recieve */ #define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 +/* Bitfields in PHYTMAC_TXSTARTSEL */ +#define PHYTMAC_TSTARTSEL_ENABLE_INDEX 0 /* Enable Tstart_sel */ +#define PHYTMAC_TSTARTSEL_ENABLE_WIDTH 1 + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 25f1ba6c1d6ef4..b8af75b3d93412 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #include #include #include @@ -13,17 +15,19 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_v2.h" -static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, - u16 cmd_subid, void *data, int len, int wait) +static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, + u16 cmd_subid, void *data, int len, int wait) { int index = 0; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; int ret = 0; + mutex_lock(&pdata->msg_ring.msg_mutex); ++pdata->msg_ring.tx_msg_tail; if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) pdata->msg_ring.tx_msg_tail = 1; @@ -32,17 +36,17 @@ static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); - msg.module_id = PHYTMAC_MODULE_ID_GMAC; - msg.cmd_id = cmd_id; + msg.cmd_type = cmd_id; msg.cmd_subid = cmd_subid; - msg.flags = PHYTMAC_FLAGS_MSG_NOINT; + msg.status0 = PHYTMAC_FLAGS_MSG_NOINT; if (len) memcpy(&msg.para[0], data, len); if (netif_msg_hw(pdata)) { - netdev_info(pdata->ndev, "tx msg: cmdid=%d, subid=%d, flags=%d, len=%d, tail=%d\n", - msg.cmd_id, msg.cmd_subid, msg.flags, len, pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d\n", + msg.cmd_type, msg.cmd_subid, msg.status0, len, + pdata->msg_ring.tx_msg_tail); } memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); @@ -51,16 +55,17 @@ static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); - while (!(msg_rx.flags & 0x1)) { + while (!(msg_rx.status0 & PHYTMAC_CMD_PRC_COMPLETED)) { cpu_relax(); memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); } } + mutex_unlock(&pdata->msg_ring.msg_mutex); return ret; } -void phytmac_reset_hw(struct phytmac *pdata) +static void phytmac_v2_reset_hw(struct phytmac *pdata) { int q; u16 cmd_id, cmd_subid; @@ -70,26 +75,27 @@ void phytmac_reset_hw(struct phytmac *pdata) for (q = 0; q < pdata->queues_max_num; ++q) { PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(q), -1); PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(q), -1); - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_RX_PTR(q), 0); } /* reset hw rx/tx enable */ cmd_id = PHYTMAC_MSG_CMD_DEFAULT; cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_HW; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); /* reset tx ring */ memset(&ring, 0, sizeof(ring)); ring.queue_num = pdata->queues_max_num; cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); /* reset rx ring */ cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); } -static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +static int phytmac_v2_get_mac_addr(struct phytmac *pdata, u8 *addr) { int index; u16 cmd_id, cmd_subid; @@ -97,7 +103,7 @@ static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) @@ -115,7 +121,7 @@ static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) return 0; } -int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +static int phytmac_v2_set_mac_addr(struct phytmac *pdata, const u8 *addr) { u16 cmd_id; u16 cmd_subid; @@ -127,46 +133,70 @@ int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) para.addrl = cpu_to_le32(*((u32 *)addr)); para.addrh = cpu_to_le16(*((u16 *)(addr + 4))); - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_v2_pcs_software_reset(struct phytmac *pdata, int reset) +{ + u16 cmd_id; + u16 cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (reset) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PCS_RESET; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET; + + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); return 0; } -static int phytmac_init_hw(struct phytmac *pdata) +static int phytmac_v2_init_hw(struct phytmac *pdata) { u16 cmd_id, cmd_subid; struct phytmac_dma_info dma; struct phytmac_eth_info eth; + u8 mdc; + + if (pdata->mii_bus) { + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } - phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + phytmac_v2_set_mac_addr(pdata, pdata->ndev->dev_addr); cmd_id = PHYTMAC_MSG_CMD_SET; if (pdata->capacities & PHYTMAC_CAPS_JUMBO) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO; else cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); if (pdata->ndev->flags & IFF_PROMISC) { cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } if (pdata->ndev->features & NETIF_F_RXCSUM) { cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } - if (!(pdata->ndev->flags & IFF_BROADCAST)) { + if (pdata->ndev->flags & IFF_BROADCAST) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_BC; + else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_BC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); - } + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); memset(&dma, 0, sizeof(dma)); cmd_subid = PHYTMAC_MSG_CMD_SET_DMA; @@ -177,22 +207,31 @@ static int phytmac_init_hw(struct phytmac *pdata) dma.hw_dma_cap |= HW_DMA_CAP_CSUM; if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) dma.hw_dma_cap |= HW_DMA_CAP_PTP; + if (pdata->dma_data_width == PHYTMAC_DBW32) + dma.hw_dma_cap |= HW_DMA_CAP_DDW32; if (pdata->dma_data_width == PHYTMAC_DBW64) dma.hw_dma_cap |= HW_DMA_CAP_DDW64; if (pdata->dma_data_width == PHYTMAC_DBW128) dma.hw_dma_cap |= HW_DMA_CAP_DDW128; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + + cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; + mdc = PHYTMAC_CLK_DIV96; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 1); memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; eth.index = 0; eth.etype = (uint16_t)ETH_P_IP; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + + if (phy_interface_mode_is_8023z(pdata->phy_interface)) + phytmac_v2_pcs_software_reset(pdata, 1); return 0; } -static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_multicast(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -202,11 +241,11 @@ static int phytmac_enable_multicast(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +static int phytmac_v2_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) { u16 cmd_id, cmd_subid; struct phytmac_mc_info para; @@ -216,12 +255,12 @@ static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC; para.mc_bottom = (u32)mc_filter[0]; para.mc_top = (u32)mc_filter[1]; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_init_ring_hw(struct phytmac *pdata) +static int phytmac_v2_init_ring_hw(struct phytmac *pdata) { u16 cmd_id, cmd_subid; struct phytmac_ring_info rxring; @@ -240,27 +279,27 @@ static int phytmac_init_ring_hw(struct phytmac *pdata) txring.hw_dma_cap |= HW_DMA_CAP_64B; rxring.hw_dma_cap |= HW_DMA_CAP_64B; for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), queue->tx_head); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(q), queue->tx_head); txring.addr[q] = queue->tx_ring_addr; rxring.addr[q] = queue->rx_ring_addr; } - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE; rxbuf.queue_num = pdata->queues_num; rxbuf.buffer_size = pdata->rx_buffer_len / 64; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); return 0; } -int phytmac_init_msg_ring(struct phytmac *pdata) +static int phytmac_v2_init_msg_ring(struct phytmac *pdata) { u32 size = 0; @@ -278,7 +317,7 @@ int phytmac_init_msg_ring(struct phytmac *pdata) return 0; } -static int phytmac_get_feature_all(struct phytmac *pdata) +static int phytmac_v2_get_feature_all(struct phytmac *pdata) { u16 cmd_id, cmd_subid; int index; @@ -287,7 +326,7 @@ static int phytmac_get_feature_all(struct phytmac *pdata) memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) @@ -317,25 +356,26 @@ static int phytmac_get_feature_all(struct phytmac *pdata) return 0; } -void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +static void phytmac_v2_get_regs(struct phytmac *pdata, u32 *reg_buff) { u16 cmd_id, cmd_subid; - u16 reg_num; int index; + u8 interface; cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_REG_READ; - reg_num = 16; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)®_num, 2, 1); + cmd_subid = PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL; + interface = pdata->phytmac_v2_interface; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&interface), sizeof(interface), 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) index += pdata->msg_ring.tx_msg_ring_size; - memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + READ_REG_NUM_MAX * sizeof(u32)); } -static void phytmac_get_hw_stats(struct phytmac *pdata) +static void phytmac_v2_get_hw_stats(struct phytmac *pdata) { u16 cmd_id, cmd_subid; u8 count; @@ -346,27 +386,20 @@ static void phytmac_get_hw_stats(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 1; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); - - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 2; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + /* There are 45 registers in total, read 16 regs at a time, read 13 regs at last time */ + for (i = 1; i <= 3; i++) { + count = i; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 3; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - - for (i = 0; i < 3; i++) { - index = pdata->msg_ring.tx_msg_tail + i - 2; + index = pdata->msg_ring.tx_msg_tail; if (index <= 0) index += pdata->msg_ring.tx_msg_ring_size; - memcpy(&stats[i * 16], pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + + memcpy(&stats[(i - 1) * READ_REG_NUM_MAX], pdata->msg_regs + PHYTMAC_MSG(index) + + MSG_HDR_LEN, sizeof(u32) * READ_REG_NUM_MAX); } - for (i = 0, j = 0; i < 44; i++) { + for (i = 0, j = 0; i < 45; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; @@ -385,7 +418,7 @@ static void phytmac_get_hw_stats(struct phytmac *pdata) } } -static void phytmac_mdio_idle(struct phytmac *pdata) +static void phytmac_v2_mdio_idle(struct phytmac *pdata) { u32 val; @@ -397,7 +430,7 @@ static void phytmac_mdio_idle(struct phytmac *pdata) } } -static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) +static int phytmac_v2_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) { u16 data; @@ -406,14 +439,14 @@ static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int reg | PHYTMAC_BITS(PHYADDR, mii_id) | PHYTMAC_BITS(REGADDR, regnum) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return data; } -static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, - int regnum, u16 data) +static int phytmac_v2_mdio_data_write_c22(struct phytmac *pdata, int mii_id, + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_WRITE) @@ -421,12 +454,12 @@ static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, | PHYTMAC_BITS(REGADDR, regnum) | PHYTMAC_BITS(VALUE, data) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return 0; } -static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) +static int phytmac_v2_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) { u16 data; @@ -436,21 +469,21 @@ static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int dev | PHYTMAC_BITS(REGADDR, devad & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_READ) | PHYTMAC_BITS(PHYADDR, mii_id) | PHYTMAC_BITS(REGADDR, devad & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return data; } -static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, - int regnum, u16 data) +static int phytmac_v2_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) @@ -458,24 +491,62 @@ static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int de | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_WRITE) | PHYTMAC_BITS(PHYADDR, mii_id) | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) | PHYTMAC_BITS(VALUE, data) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return 0; } -static int phytmac_powerup_hw(struct phytmac *pdata, int on) +static int phytmac_v2_powerup_hw(struct phytmac *pdata, int on) { u32 status, data0, data1, rdata1; int ret; + acpi_handle handle; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status acpi_sts; + unsigned long long rv; + + if (!(pdata->capacities & PHYTMAC_CAPS_PWCTRL)) { + pdata->power_state = on; + return 0; + } - if (pdata->capacities & PHYTMAC_CAPS_LPI) { + if (has_acpi_companion(pdata->dev)) { + handle = ACPI_HANDLE(pdata->dev); + + netdev_info(pdata->ndev, "set gmac power %s\n", + on == PHYTMAC_POWERON ? "on" : "off"); + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + + if (on == PHYTMAC_POWERON) { + acpi_sts = acpi_evaluate_integer(handle, "PPWO", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWO Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power on\n"); + } else { + acpi_sts = acpi_evaluate_integer(handle, "PPWD", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWD Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power off\n"); + } + } else { ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, 1, PHYTMAC_TIMEOUT); if (ret) @@ -513,10 +584,10 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } pdata->power_state = on; @@ -524,20 +595,32 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) return 0; } -static int phytmac_set_wake(struct phytmac *pdata, int wake) +static int phytmac_v2_set_wake(struct phytmac *pdata, int wake) { u16 cmd_id, cmd_subid; - u8 wol = (u8)wake; + struct phytmac_wol para; + u32 wol_type = 0; + + if (wake & PHYTMAC_WAKE_MAGIC) + wol_type |= PHYTMAC_BIT(MAGIC); + if (wake & PHYTMAC_WAKE_ARP) + wol_type |= PHYTMAC_BIT(ARP); + if (wake & PHYTMAC_WAKE_UCAST) + wol_type |= PHYTMAC_BIT(UCAST); + if (wake & PHYTMAC_WAKE_MCAST) + wol_type |= PHYTMAC_BIT(MCAST); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_WOL; - - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&wol), 1, 1); + memset(¶, 0, sizeof(para)); + para.wol_type = cpu_to_le32(wol_type); + para.wake = (u8)wake; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_enable_promise(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_promise(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; u8 rxcsum = 0; @@ -551,12 +634,12 @@ static int phytmac_enable_promise(struct phytmac *pdata, int enable) rxcsum = 1; } - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); return 0; } -static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_rxcsum(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -566,12 +649,12 @@ static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_txcsum(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -581,12 +664,12 @@ static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_mdio(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -596,12 +679,12 @@ static int phytmac_enable_mdio(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MDIO; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_autoneg(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -611,13 +694,13 @@ static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); pdata->autoneg = enable; return 0; } -static int phytmac_enable_pause(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_pause(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -627,12 +710,12 @@ static int phytmac_enable_pause(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +static int phytmac_v2_enable_network(struct phytmac *pdata, int enable, int rx_tx) { u16 cmd_id, cmd_subid; @@ -642,12 +725,12 @@ static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +static int phytmac_v2_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) { struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; struct phytmac_fdir_info fdir; @@ -675,19 +758,19 @@ static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_ fdir.srcport_mask = tp4sp_m->psrc; } - fdir.location = rx_flow->location; - fdir.queue = rx_flow->ring_cookie; + fdir.location = (u8)(rx_flow->location); + fdir.queue = (u8)(rx_flow->ring_cookie); if (fdir.ipsrc_en || fdir.ipdst_en || fdir.port_en) { cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_ADD_FDIR; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); } return 0; } -static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +static int phytmac_v2_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) { struct phytmac_fdir_info fdir; u16 cmd_id, cmd_subid; @@ -695,21 +778,21 @@ static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_ memset(&fdir, 0, sizeof(fdir)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_DEL_FDIR; - fdir.location = (u8)rx_flow->location; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + fdir.location = (u8)(rx_flow->location); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); return 0; } -static void phytmac_tx_start(struct phytmac_queue *queue) +static void phytmac_v2_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(queue->index), queue->tx_tail); queue->tx_xmit_more = 0; } -static u32 phytmac_get_irq_mask(u32 mask) +static u32 phytmac_v2_get_irq_mask(u32 mask) { u32 value = 0; @@ -722,7 +805,7 @@ static u32 phytmac_get_irq_mask(u32 mask) return value; } -static u32 phytmac_get_irq_status(u32 value) +static u32 phytmac_v2_get_irq_status(u32 value) { u32 status = 0; @@ -735,111 +818,124 @@ static u32 phytmac_get_irq_status(u32 value) return status; } -static void phytmac_enable_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_ER(queue_index), value); } -static void phytmac_disable_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(queue_index), value); } -static void phytmac_clear_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(queue_index), value); } -static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +static unsigned int phytmac_v2_get_irq(struct phytmac *pdata, int queue_index) { u32 status; u32 value; value = PHYTMAC_READ(pdata, PHYTMAC_INT_SR(queue_index)); - status = phytmac_get_irq_status(value); + status = phytmac_v2_get_irq_status(value); return status; } -static void phytmac_interface_config(struct phytmac *pdata, unsigned int mode, - const struct phylink_link_state *state) +static void phytmac_v2_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; + u8 autoneg = 0; + + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + if (mode == MLO_AN_FIXED) + autoneg = 0; + else + autoneg = 1; + } + + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + autoneg = 1; + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + autoneg = 0; memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; - cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_CONFIG; - para.interface = state->interface; - para.autoneg = (mode == MLO_AN_FIXED ? 0 : 1); + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_MAC_CONFIG; + para.interface = pdata->phytmac_v2_interface; + para.autoneg = autoneg; para.speed = state->speed; para.duplex = state->duplex; pdata->autoneg = para.autoneg; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); } -static int phytmac_interface_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) +static int phytmac_v2_interface_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; + if (interface == PHY_INTERFACE_MODE_SGMII) { + if (speed == SPEED_2500) + pdata->autoneg = 0; + } + memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG; - para.interface = interface; + para.interface = pdata->phytmac_v2_interface; para.duplex = duplex; para.speed = speed; para.autoneg = pdata->autoneg; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_interface_linkdown(struct phytmac *pdata) +static int phytmac_v2_interface_linkdown(struct phytmac *pdata) { return 0; } -static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) +static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) { - struct phytmac_interface_info para; u16 cmd_id, cmd_subid; if (interface == PHY_INTERFACE_MODE_USXGMII || interface == PHY_INTERFACE_MODE_10GBASER) { - memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; - para.interface = interface; - para.duplex = duplex; - para.speed = speed; - para.autoneg = 0; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } return 0; } -static int phytmac_pcs_linkdown(struct phytmac *pdata) +static int phytmac_v2_pcs_linkdown(struct phytmac *pdata) { return 0; } -static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) +static unsigned int phytmac_v2_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || interface == PHY_INTERFACE_MODE_1000BASEX || @@ -852,8 +948,8 @@ static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t return 0; } -static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, - u32 tx_tail, struct packet_info *packet) +static unsigned int phytmac_v2_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) { unsigned int i, ctrl; struct phytmac *pdata = queue->pdata; @@ -896,8 +992,8 @@ static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, return 0; } -static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, - u32 index) +static void phytmac_v2_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) { struct phytmac_dma_desc *desc; @@ -909,7 +1005,7 @@ static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, desc->desc0 |= PHYTMAC_BIT(RXUSED); } -static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) +static unsigned int phytmac_v2_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) { struct phytmac *pdata = queue->pdata; struct phytmac_dma_desc *desc; @@ -922,19 +1018,21 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); /* Make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 = lower_32_bits(addr); } else { desc->desc1 = 0; /* make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 &= ~PHYTMAC_BIT(RXUSED); } return 0; } -static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +static unsigned int phytmac_v2_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; desc->desc0 = PHYTMAC_BIT(RXUSED); @@ -942,12 +1040,29 @@ static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) return 0; } -static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +static unsigned int phytmac_v2_zero_tx_desc(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = 0; + desc->desc1 &= ~PHYTMAC_BIT(TXUSED); + + return 0; +} + +static void phytmac_v2_update_rx_tail(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_PTR(queue->index), queue->rx_head); +} + +static int phytmac_v2_tx_complete(const struct phytmac_dma_desc *desc) { return PHYTMAC_GET_BITS(desc->desc1, TXUSED); } -static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_complete(const struct phytmac_dma_desc *desc) { dma_addr_t addr; bool used; @@ -962,7 +1077,7 @@ static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) return false; } -static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +static int phytmac_v2_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) { if (pdata->capacities & PHYTMAC_CAPS_JUMBO) return desc->desc1 & PHYTMAC_RXJFRMLEN_MASK; @@ -970,7 +1085,7 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; } -static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; u32 check = value >> PHYTMAC_RXCSUM_INDEX & 0x3; @@ -978,28 +1093,28 @@ static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) return (check == PHYTMAC_RXCSUM_IP_TCP || check == PHYTMAC_RXCSUM_IP_UDP); } -static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_single_buffer(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return ((value & PHYTMAC_BIT(RXSOF)) && (value & PHYTMAC_BIT(RXEOF))); } -static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_sof(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return (value & PHYTMAC_BIT(RXSOF)); } -static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_eof(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return (value & PHYTMAC_BIT(RXEOF)); } -static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +static void phytmac_v2_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) { unsigned int frag; unsigned int tmp = end; @@ -1014,7 +1129,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en } } -static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +static void phytmac_v2_clear_tx_desc(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; struct phytmac_dma_desc *desc = NULL; @@ -1031,10 +1146,10 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) desc->desc1 = PHYTMAC_BIT(TXUSED); } desc->desc1 |= PHYTMAC_BIT(TXWRAP); - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(queue->index), queue->tx_tail); } -static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +static void phytmac_v2_get_time(struct phytmac *pdata, struct timespec64 *ts) { u32 ns, secl, sech; @@ -1046,7 +1161,7 @@ static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) ts->tv_sec = (((u64)sech << 32) | secl) & TIMER_SEC_MAX_VAL; } -void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +static void phytmac_v2_set_time(struct phytmac *pdata, time64_t sec, long nsec) { u32 secl, sech; @@ -1059,7 +1174,7 @@ void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, nsec); } -void phytmac_clear_time(struct phytmac *pdata) +static void phytmac_v2_clear_time(struct phytmac *pdata) { u32 value; @@ -1077,7 +1192,7 @@ void phytmac_clear_time(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, 0); } -int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +static int phytmac_v2_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) { u16 cmd_id, cmd_subid; struct phytmac_ts_config para; @@ -1087,12 +1202,12 @@ int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) para.tx_mode = ctrl->tx_control; para.rx_mode = ctrl->rx_control; para.one_step = ctrl->one_step; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +static int phytmac_v2_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) { u32 value; @@ -1103,17 +1218,17 @@ static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) return 0; } -static void phytmac_ptp_init_hw(struct phytmac *pdata) +static void phytmac_v2_ptp_init_hw(struct phytmac *pdata) { struct timespec64 ts; ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); - phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + phytmac_v2_set_time(pdata, ts.tv_sec, ts.tv_nsec); - phytmac_set_tsincr(pdata, &pdata->ts_incr); + phytmac_v2_set_tsincr(pdata, &pdata->ts_incr); } -static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +static int phytmac_v2_adjust_fine(struct phytmac *pdata, long ppm, bool negative) { struct ts_incr ts_incr; u32 tmp; @@ -1133,22 +1248,25 @@ static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) & ((1 << PHYTMAC_INCR_NSEC_WIDTH) - 1); ts_incr.sub_ns = adj & ((1 << PHYTMAC_INCR_SNSEC_WIDTH) - 1); - phytmac_set_tsincr(pdata, &ts_incr); + phytmac_v2_set_tsincr(pdata, &ts_incr); return 0; } -int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +static int phytmac_v2_adjust_time(struct phytmac *pdata, s64 delta, int neg) { u32 adj; if (delta > PHYTMAC_ASEC_MAX) { struct timespec64 now, then; - then = ns_to_timespec64(delta); - phytmac_get_time(pdata, &now); + if (neg) + then = ns_to_timespec64(-delta); + else + then = ns_to_timespec64(delta); + phytmac_v2_get_time(pdata, &now); now = timespec64_add(now, then); - phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + phytmac_v2_set_time(pdata, now.tv_sec, now.tv_nsec); } else { adj = (neg << PHYTMAC_AADD_INDEX) | delta; PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, adj); @@ -1157,7 +1275,7 @@ int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) return 0; } -static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +static int phytmac_v2_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) { int ts_valid = 0; @@ -1168,7 +1286,7 @@ static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc return ts_valid; } -static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +static void phytmac_v2_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) { struct timespec64 ts2; @@ -1176,7 +1294,7 @@ static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct PHYTMAC_GET_BITS(ts_1, TS_SECL); ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, TS_NSEC); - phytmac_get_time(pdata, &ts2); + phytmac_v2_get_time(pdata, &ts2); if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_TS_SEC_TOP >> 1)) != 0) ts->tv_sec -= PHYTMAC_TS_SEC_TOP; @@ -1184,80 +1302,82 @@ static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_TS_SEC_MASK)); } -static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +static unsigned int phytmac_v2_get_ts_rate(struct phytmac *pdata) { return 300000000; } struct phytmac_hw_if phytmac_2p0_hw = { - .init_msg_ring = phytmac_init_msg_ring, - .reset_hw = phytmac_reset_hw, - .init_hw = phytmac_init_hw, - .init_ring_hw = phytmac_init_ring_hw, - .get_feature = phytmac_get_feature_all, - .get_regs = phytmac_get_regs, - .get_stats = phytmac_get_hw_stats, - .set_mac_address = phytmac_set_mac_addr, - .get_mac_address = phytmac_get_mac_addr, - .mdio_read = phytmac_mdio_data_read_c22, - .mdio_write = phytmac_mdio_data_write_c22, - .mdio_read_c45 = phytmac_mdio_data_read_c45, - .mdio_write_c45 = phytmac_mdio_data_write_c45, - .poweron = phytmac_powerup_hw, - .set_wol = phytmac_set_wake, - .enable_promise = phytmac_enable_promise, - .enable_multicast = phytmac_enable_multicast, - .set_hash_table = phytmac_set_mc_hash, - .enable_rx_csum = phytmac_enable_rxcsum, - .enable_tx_csum = phytmac_enable_txcsum, - .enable_mdio_control = phytmac_enable_mdio, - .enable_autoneg = phytmac_enable_autoneg, - .enable_pause = phytmac_enable_pause, - .enable_network = phytmac_enable_network, - .add_fdir_entry = phytmac_add_fdir_entry, - .del_fdir_entry = phytmac_del_fdir_entry, + .init_msg_ring = phytmac_v2_init_msg_ring, + .reset_hw = phytmac_v2_reset_hw, + .init_hw = phytmac_v2_init_hw, + .init_ring_hw = phytmac_v2_init_ring_hw, + .get_feature = phytmac_v2_get_feature_all, + .get_regs = phytmac_v2_get_regs, + .get_stats = phytmac_v2_get_hw_stats, + .set_mac_address = phytmac_v2_set_mac_addr, + .get_mac_address = phytmac_v2_get_mac_addr, + .mdio_read = phytmac_v2_mdio_data_read_c22, + .mdio_write = phytmac_v2_mdio_data_write_c22, + .mdio_read_c45 = phytmac_v2_mdio_data_read_c45, + .mdio_write_c45 = phytmac_v2_mdio_data_write_c45, + .poweron = phytmac_v2_powerup_hw, + .set_wol = phytmac_v2_set_wake, + .enable_promise = phytmac_v2_enable_promise, + .enable_multicast = phytmac_v2_enable_multicast, + .set_hash_table = phytmac_v2_set_mc_hash, + .enable_rx_csum = phytmac_v2_enable_rxcsum, + .enable_tx_csum = phytmac_v2_enable_txcsum, + .enable_mdio_control = phytmac_v2_enable_mdio, + .enable_autoneg = phytmac_v2_enable_autoneg, + .enable_pause = phytmac_v2_enable_pause, + .enable_network = phytmac_v2_enable_network, + .add_fdir_entry = phytmac_v2_add_fdir_entry, + .del_fdir_entry = phytmac_v2_del_fdir_entry, /* mac config */ - .mac_config = phytmac_interface_config, - .mac_linkup = phytmac_interface_linkup, - .mac_linkdown = phytmac_interface_linkdown, - .pcs_linkup = phytmac_pcs_linkup, - .pcs_linkdown = phytmac_pcs_linkdown, - .get_link = phytmac_pcs_get_link, + .mac_config = phytmac_v2_interface_config, + .mac_linkup = phytmac_v2_interface_linkup, + .mac_linkdown = phytmac_v2_interface_linkdown, + .pcs_linkup = phytmac_v2_pcs_linkup, + .pcs_linkdown = phytmac_v2_pcs_linkdown, + .get_link = phytmac_v2_pcs_get_link, /* irq */ - .enable_irq = phytmac_enable_irq, - .disable_irq = phytmac_disable_irq, - .clear_irq = phytmac_clear_irq, - .get_irq = phytmac_get_irq, + .enable_irq = phytmac_v2_enable_irq, + .disable_irq = phytmac_v2_disable_irq, + .clear_irq = phytmac_v2_clear_irq, + .get_irq = phytmac_v2_get_irq, /* tx and rx */ - .tx_map = phytmac_tx_map_desc, - .transmit = phytmac_tx_start, - .tx_complete = phytmac_tx_complete, - .rx_complete = phytmac_rx_complete, - .get_rx_pkt_len = phytmac_rx_pkt_len, - .init_rx_map = phytmac_init_rx_map_desc, - .rx_map = phytmac_rx_map_desc, - .rx_checksum = phytmac_rx_checksum, - .rx_single_buffer = phytmac_rx_single_buffer, - .rx_pkt_start = phytmac_rx_sof, - .rx_pkt_end = phytmac_rx_eof, - .clear_rx_desc = phytmac_clear_rx_desc, - .clear_tx_desc = phytmac_clear_tx_desc, - .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, + .tx_map = phytmac_v2_tx_map_desc, + .transmit = phytmac_v2_tx_start, + .update_rx_tail = phytmac_v2_update_rx_tail, + .tx_complete = phytmac_v2_tx_complete, + .rx_complete = phytmac_v2_rx_complete, + .get_rx_pkt_len = phytmac_v2_rx_pkt_len, + .init_rx_map = phytmac_v2_init_rx_map_desc, + .rx_map = phytmac_v2_rx_map_desc, + .rx_checksum = phytmac_v2_rx_checksum, + .rx_single_buffer = phytmac_v2_rx_single_buffer, + .rx_pkt_start = phytmac_v2_rx_sof, + .rx_pkt_end = phytmac_v2_rx_eof, + .clear_rx_desc = phytmac_v2_clear_rx_desc, + .clear_tx_desc = phytmac_v2_clear_tx_desc, + .zero_rx_desc_addr = phytmac_v2_zero_rx_desc_addr, + .zero_tx_desc = phytmac_v2_zero_tx_desc, /* ptp */ - .init_ts_hw = phytmac_ptp_init_hw, - .set_time = phytmac_set_time, - .clear_time = phytmac_clear_time, - .get_time = phytmac_get_time, - .set_ts_config = phytmac_set_tsmode, - .set_incr = phytmac_set_tsincr, - .adjust_fine = phytmac_adjust_fine, - .adjust_time = phytmac_adjust_time, - .ts_valid = phytmac_ts_valid, - .get_timestamp = phytmac_get_dma_ts, - .get_ts_rate = phytmac_get_ts_rate, + .init_ts_hw = phytmac_v2_ptp_init_hw, + .set_time = phytmac_v2_set_time, + .clear_time = phytmac_v2_clear_time, + .get_time = phytmac_v2_get_time, + .set_ts_config = phytmac_v2_set_tsmode, + .set_incr = phytmac_v2_set_tsincr, + .adjust_fine = phytmac_v2_adjust_fine, + .adjust_time = phytmac_v2_adjust_time, + .ts_valid = phytmac_v2_ts_valid, + .get_timestamp = phytmac_v2_get_dma_ts, + .get_ts_rate = phytmac_v2_get_ts_rate, }; EXPORT_SYMBOL_GPL(phytmac_2p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 92e4806ac2c1f7..d2da4acb69a7d5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_V2_H #define _PHYTMAC_V2_H extern struct phytmac_hw_if phytmac_2p0_hw; +#define PHYTMAC_CMD_PRC_COMPLETED 0x1 #define PHYTMAC_MSG_SRAM_SIZE 4096 -#define MSG_HDR_LEN 8 +#define MSG_HDR_LEN 8 +#define READ_REG_NUM_MAX 16 #define PHYTMAC_TX_MSG_HEAD 0x000 #define PHYTMAC_TX_MSG_TAIL 0x004 @@ -83,7 +87,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define TIMER_SEC_MAX_VAL (((u64)1 << PHYTMAC_TIMER_SEC_WIDTH) - 1) #define TIMER_NSEC_MAX_VAL ((1 << PHYTMAC_TIMER_NSEC_WIDTH) - 1) -#define PHYTMAC_TAIL_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_TX_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_RX_PTR(i) (0x0030 + ((i) * 4)) #define PHYTMAC_INT_ER(i) (0x0140 + ((i) * 4)) #define PHYTMAC_INT_DR(i) (0x0180 + ((i) * 4)) #define PHYTMAC_INT_MR(i) (0x01c0 + ((i) * 4)) @@ -200,15 +205,38 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_TS_SEC_MASK 0x3f #define PHYTMAC_TS_SEC_TOP 0x40 +/* WOL register */ +#define PHYTMAC_ARP_IP_INDEX 0 +#define PHYTMAC_ARP_IP_WIDTH 16 +#define PHYTMAC_MAGIC_INDEX 16 +#define PHYTMAC_MAGIC_WIDTH 1 +#define PHYTMAC_ARP_INDEX 17 +#define PHYTMAC_ARP_WIDTH 1 +#define PHYTMAC_UCAST_INDEX 18 +#define PHYTMAC_UCAST_WIDTH 1 +#define PHYTMAC_MCAST_INDEX 19 +#define PHYTMAC_MCAST_WIDTH 1 + #define HW_DMA_CAP_64B 0x1 #define HW_DMA_CAP_CSUM 0x2 #define HW_DMA_CAP_PTP 0x4 -#define HW_DMA_CAP_DDW64 0x8 -#define HW_DMA_CAP_DDW128 0x10 +#define HW_DMA_CAP_DDW32 0x8 +#define HW_DMA_CAP_DDW64 0x10 +#define HW_DMA_CAP_DDW128 0x20 +#define PHYTMAC_DBW32 1 #define PHYTMAC_DBW64 2 #define PHYTMAC_DBW128 4 +#define PHYTMAC_CLK_DIV8 0 +#define PHYTMAC_CLK_DIV16 1 +#define PHYTMAC_CLK_DIV32 2 +#define PHYTMAC_CLK_DIV48 3 +#define PHYTMAC_CLK_DIV64 4 +#define PHYTMAC_CLK_DIV96 5 +#define PHYTMAC_CLK_DIV128 6 +#define PHYTMAC_CLK_DIV224 7 + enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, PHYTMAC_MSG_CMD_SET, @@ -228,7 +256,7 @@ enum phytmac_set_subid { PHYTMAC_MSG_CMD_SET_INIT_RING = 1, PHYTMAC_MSG_CMD_SET_INIT_TX_RING = 2, PHYTMAC_MSG_CMD_SET_INIT_RX_RING = 3, - PHYTMAC_MSG_CMD_SET_MAC_CONFIG = 4, + PHYTMAC_MSG_CMD_SET_INIT_MAC_CONFIG = 4, PHYTMAC_MSG_CMD_SET_ADDR = 5, PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE = 6, PHYTMAC_MSG_CMD_SET_DMA = 7, @@ -269,6 +297,13 @@ enum phytmac_set_subid { PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG = 42, PHYTMAC_MSG_CMD_SET_RX_DATA_OFFSET = 43, PHYTMAC_MSG_CMD_SET_WOL = 44, + PHYTMAC_MSG_CMD_SET_ENABLE_RSC = 45, + PHYTMAC_MSG_CMD_SET_DISABLE_RSC = 46, + PHYTMAC_MSG_CMD_SET_ENABLE_TX_START = 47, + PHYTMAC_MSG_CMD_SET_ENABLE_PCS_RESET = 48, + PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET = 49, + PHYTMAC_MSG_CMD_SET_MDC = 50, + PHYTMAC_MSG_CMD_SET_OUTSTANDING = 51, }; enum phytmac_get_subid { @@ -278,6 +313,8 @@ enum phytmac_get_subid { PHYTMAC_MSG_CMD_GET_BD_PREFETCH, PHYTMAC_MSG_CMD_GET_STATS, PHYTMAC_MSG_CMD_GET_REG_READ, + PHYTMAC_MSG_CMD_GET_RX_FLOW, + PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL, }; struct phytmac_interface_info { @@ -292,6 +329,11 @@ struct phytmac_mc_info { u32 mc_top; } __packed; +struct phytmac_reg_info { + u32 offset; + u16 regnum; +} __packed; + struct phytmac_fdir_info { u32 ip4src; u32 ip4dst; @@ -351,12 +393,26 @@ struct phytmac_feature { u8 max_rx_fs; } __packed; +struct phytmac_wol { + u32 wol_type; + u8 wake; +} __packed; + struct phytmac_msg_info { - u16 module_id; - u16 cmd_id; - u16 cmd_subid; - u16 flags; + u8 reserved; + u8 seq; + u8 cmd_type; + u8 cmd_subid; + u16 len; + u8 status1; + u8 status0; u8 para[64]; } __packed; +struct phytmac_ots_config { + u32 axi_rd; + u32 axi_wr; + u8 queuenum; +} __packed; + #endif From 5be17c9f6aaa6accce95241d50c84f50f7730dce Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 329/521] PHYTIUM: net/phytmac: Slove left-shift out of bound issue When the value of the bd_prefetch is equal to 0, subtracting 1 and shifting left will cause the overflow issue. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Idc6b8f8100c62331ca6e9778082c533221558e8a Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/ec5f2c26458fcf71564603c01901a717eeddee19 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 3806e09bc1220a..4d66ae498b88fe 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.29" +#define PHYTMAC_DRIVER_VERSION "1.0.39" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index b8af75b3d93412..9bab3d2a5ca4d5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -342,10 +342,13 @@ static int phytmac_v2_get_feature_all(struct phytmac *pdata) pdata->dma_addr_width = 32; pdata->dma_data_width = para.dma_data_width; pdata->max_rx_fs = para.max_rx_fs; - pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * - sizeof(struct phytmac_dma_desc); - pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * - sizeof(struct phytmac_dma_desc); + + if (para.tx_bd_prefetch) + pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + if (para.rx_bd_prefetch) + pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); if (netif_msg_hw(pdata)) { netdev_info(pdata->ndev, "feature qnum=%d, daw=%d, dbw=%d, rxfs=%d, rxbd=%d, txbd=%d\n", From cd823b877266a142ee2732aeb57db1e1de82fc3e Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 30 May 2025 10:29:38 +0800 Subject: [PATCH 330/521] PHYTIUM: Revert "net: phytmac: Manage WOL on MAC if PHY supports WOL" This reverts commit 94967211afe596a9e9c5d80ca9d54e915de3a335. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/15076a68a5a825e0275fb71dea20e9a0a1d54057 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index e05274b961e4b1..7e0c749477914d 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -117,8 +117,7 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) ret = phylink_ethtool_set_wol(pdata->phylink, wol); - /* Don't manage WoL on MAC, if PHY set_wol() fails */ - if (ret && ret != -EOPNOTSUPP) + if (!ret || ret != -EOPNOTSUPP) return ret; pdata->wol = 0; From 4bd893038848fe258b2bb6ffc3de359ec1d55ef5 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 30 May 2025 10:36:36 +0800 Subject: [PATCH 331/521] PHYTIUM: Revert "net/phytmac: Bugfix set WOL failed issue" This reverts commit 3161ed880479e35c7759e127c62c37d2d722edee. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/bbbe8cf7d988f8c0cd1369b8aa4700541444226a Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 7e0c749477914d..23c46bd91f2630 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -90,23 +90,23 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); - wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); - wol->supported = WAKE_MAGIC | WAKE_ARP | - WAKE_UCAST | WAKE_MCAST; - if (pdata->wol & PHYTMAC_WAKE_MAGIC) { wol->wolopts |= WAKE_MAGIC; + wol->supported |= WAKE_MAGIC; } if (pdata->wol & PHYTMAC_WAKE_ARP) { wol->wolopts |= WAKE_ARP; + wol->supported |= WAKE_ARP; } if (pdata->wol & PHYTMAC_WAKE_UCAST) { wol->wolopts |= WAKE_UCAST; + wol->supported |= WAKE_UCAST; } if (pdata->wol & PHYTMAC_WAKE_MCAST) { wol->wolopts |= WAKE_MCAST; + wol->supported |= WAKE_MCAST; } } From 25d8568fa70600ea387239aa4bd9478b4a06b696 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 332/521] PHYTIUM: net/phytmac: Manage WOL on MAC if PHY supports WOL feature Don't manage WoL on MAC, if PHY sets wol fails, So modify if statement to handle abnormal scenarios correctly. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I54cb5ea7f3a51d1b5c4768c6f15fd7e3bb6502ae Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/3b966d55109f329286fb6c3da0c57ac4a0fa5bfa Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 4d66ae498b88fe..2c9f2966071c17 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.39" +#define PHYTMAC_DRIVER_VERSION "1.0.40" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ From c0521b8129bf1c0f8ffb76a4760135c298917e1f Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 333/521] PHYTIUM: net/phytmac: Fixed the PTP test failure issue The bit of RX_TS_VALID should be retained in the process of zero RX address description, which indicates a valid timestamp in the BD entry. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I9b6b89ea3bb9907d2a929e4fa630b6cf28dac4ef Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f1ff6d0c777103aba315cc3427f4b6dc5f18560b Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v1.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 2c9f2966071c17..1351f8f7a89535 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.40" +#define PHYTMAC_DRIVER_VERSION "1.0.41" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 4fc0bfa2f696ec..a9cd9cb9a2f57a 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1003,7 +1003,7 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; - desc->desc0 = PHYTMAC_BIT(RX_USED); + desc->desc0 = (desc->desc0 & PHYTMAC_BIT(RX_TS_VALID)) | PHYTMAC_BIT(RX_USED); return 0; } diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 9bab3d2a5ca4d5..3eb4b0424bf75c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -1038,7 +1038,7 @@ static unsigned int phytmac_v2_rx_map_desc(struct phytmac_queue *queue, u32 inde static unsigned int phytmac_v2_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; - desc->desc0 = PHYTMAC_BIT(RXUSED); + desc->desc0 = (desc->desc0 & PHYTMAC_BIT(RXTSVALID)) | PHYTMAC_BIT(RXUSED); return 0; } From 13cafb873f6f4a43e32076540fb1fe20b2a1deed Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 334/521] PHYTIUM: net/phytmac: Cancels the power-on/off capability The MAC register is powered on by default in the firmware and the driver cancels the power-on and power-off capability. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6a55c577f92fe4f39bc7290861f217b7699ba9ee Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/ba2e5fa1f8e4e92786347336f7fd56c2cd4f65fc Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_platform.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 1351f8f7a89535..c53277e5663187 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.41" +#define PHYTMAC_DRIVER_VERSION "1.0.42" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 95509711894b05..eb3cc7d8f42906 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -30,7 +30,6 @@ static const struct phytmac_config phytium_2p0_config = { .hw_if = &phytmac_2p0_hw, .caps = PHYTMAC_CAPS_TAILPTR | PHYTMAC_CAPS_RXPTR - | PHYTMAC_CAPS_PWCTRL | PHYTMAC_CAPS_LSO | PHYTMAC_CAPS_MSG | PHYTMAC_CAPS_JUMBO, From 799f265b0f07186f9f2a760c58212a4f3ac63df3 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 335/521] PHYTIUM: net/phytmac: Exit probe when MDIO times out Exit early to avoid system stuck due to MDIO timeout. We have added a callback function for mdio_idle and improved its return value for judgment. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I89bd4a42402bdcab150f0808444d776ee7f7a76e Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/75f8c2f029695ed165dfcbeca5fe7cda24dd9b3f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 5 ++++- drivers/net/ethernet/phytium/phytmac_main.c | 6 ++++++ drivers/net/ethernet/phytium/phytmac_v1.c | 15 +++++++++------ drivers/net/ethernet/phytium/phytmac_v2.c | 15 +++++++++------ drivers/net/ethernet/phytium/phytmac_v2.h | 2 ++ 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index c53277e5663187..98f2cd1ba0299a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.42" +#define PHYTMAC_DRIVER_VERSION "1.0.43" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -53,6 +53,8 @@ #define DEFAULT_MSG_RING_SIZE 16 +#define PHYTMAC_MDIO_TIMEOUT 1000000 /* in usecs */ + #define PHYTMAC_CAPS_JUMBO 0x00000001 #define PHYTMAC_CAPS_PTP 0x00000002 #define PHYTMAC_CAPS_BD_RD_PREFETCH 0x00000004 @@ -534,6 +536,7 @@ struct phytmac_hw_if { int (*mdio_read)(struct phytmac *pdata, int mii_id, int regnum); int (*mdio_write)(struct phytmac *pdata, int mii_id, int regnum, u16 data); + int (*mdio_idle)(struct phytmac *pdata); int (*mdio_read_c45)(struct phytmac *pdata, int mii_id, int devad, int regnum); int (*mdio_write_c45)(struct phytmac *pdata, int mii_id, int devad, int regnum, u16 data); diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index dcede058512c65..80d3495362ad48 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1776,6 +1776,12 @@ int phytmac_mdio_register(struct phytmac *pdata) goto err_out; } + if (hw_if->mdio_idle) { + ret = hw_if->mdio_idle(pdata); + if (ret) + goto free_mdio; + } + pdata->mii_bus->name = "phytmac_mii_bus"; pdata->mii_bus->read = &phytmac_mdio_read_c22; pdata->mii_bus->write = &phytmac_mdio_write_c22; diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index a9cd9cb9a2f57a..e7ac69753dd199 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -558,16 +558,18 @@ static int phytmac_set_wake(struct phytmac *pdata, int wake) return 0; } -static void phytmac_mdio_idle(struct phytmac *pdata) +static int phytmac_mdio_idle(struct phytmac *pdata) { u32 val; + int ret; /* wait for end of transfer */ - val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); - while (!(val & PHYTMAC_BIT(MDIO_IDLE))) { - cpu_relax(); - val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); - } + ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + 1, PHYTMAC_MDIO_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mdio wait for idle time out!"); + + return ret; } static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) @@ -1416,6 +1418,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .get_stats = phytmac_get_hw_stats, .set_mac_address = phytmac_set_mac_addr, .get_mac_address = phytmac_get_mac_addr, + .mdio_idle = phytmac_mdio_idle, .mdio_read = phytmac_mdio_data_read_c22, .mdio_write = phytmac_mdio_data_write_c22, .mdio_read_c45 = phytmac_mdio_data_read_c45, diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 3eb4b0424bf75c..7b337d13329fa5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -421,16 +421,18 @@ static void phytmac_v2_get_hw_stats(struct phytmac *pdata) } } -static void phytmac_v2_mdio_idle(struct phytmac *pdata) +static int phytmac_v2_mdio_idle(struct phytmac *pdata) { u32 val; + int ret; /* wait for end of transfer */ - val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); - while (!(val & PHYTMAC_BIT(MIDLE))) { - cpu_relax(); - val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); - } + ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + 1, PHYTMAC_MDIO_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mdio wait for idle time out!"); + + return ret; } static int phytmac_v2_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) @@ -1320,6 +1322,7 @@ struct phytmac_hw_if phytmac_2p0_hw = { .get_stats = phytmac_v2_get_hw_stats, .set_mac_address = phytmac_v2_set_mac_addr, .get_mac_address = phytmac_v2_get_mac_addr, + .mdio_idle = phytmac_v2_mdio_idle, .mdio_read = phytmac_v2_mdio_data_read_c22, .mdio_write = phytmac_v2_mdio_data_write_c22, .mdio_read_c45 = phytmac_v2_mdio_data_read_c45, diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index d2da4acb69a7d5..57594732fab989 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -237,6 +237,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_CLK_DIV128 6 #define PHYTMAC_CLK_DIV224 7 +#define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) + enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, PHYTMAC_MSG_CMD_SET, From fe55ff249825aae11132f19aa5c2c7baee144f5e Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 336/521] PHYTIUM: net/phytmac: Add XDP feature support Add XDP feature support to the phytmac driver.XDP by optimizing the data processing flow provided significant performance improvements for high-performance network application. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Icebe6d715b844b4386be631c292349d0a9a70b50 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/a6378f4881507aa0a5536551fe357ed0ab9065c8 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 44 +- drivers/net/ethernet/phytium/phytmac_main.c | 444 ++++++++++++++++++-- 2 files changed, 452 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 98f2cd1ba0299a..1d89c9bd023deb 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -12,6 +12,7 @@ #include #include #include +#include #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" @@ -132,6 +133,14 @@ #define PHYTMAC_WAKE_UCAST 0x00000004 #define PHYTMAC_WAKE_MCAST 0x00000008 +/* XDP */ +#define PHYTMAC_XDP_PASS 0 +#define PHYTMAC_XDP_CONSUMED BIT(0) +#define PHYTMAC_XDP_TX BIT(1) +#define PHYTMAC_XDP_REDIR BIT(2) + +#define PHYTMAC_DESC_NEEDED (MAX_SKB_FRAGS + 4) + enum phytmac_interface { PHYTMAC_PHY_INTERFACE_MODE_NA, PHYTMAC_PHY_INTERFACE_MODE_INTERNAL, @@ -336,8 +345,20 @@ struct phytmac_dma_desc { }; #endif +/* TX resources are shared between XDP and netstack + * and we need to tag the buffer type to distinguish them + */ +enum phytmac_tx_buf_type { + PHYTMAC_TYPE_SKB = 0, + PHYTMAC_TYPE_XDP, +}; + struct phytmac_tx_skb { - struct sk_buff *skb; + union { + struct sk_buff *skb; + struct xdp_frame *xdpf; + }; + enum phytmac_tx_buf_type type; dma_addr_t addr; size_t length; bool mapped_as_page; @@ -360,6 +381,7 @@ struct phytmac_queue { struct phytmac *pdata; int irq; int index; + struct bpf_prog *xdp_prog; /* tx queue info */ unsigned int tx_head; @@ -382,6 +404,7 @@ struct phytmac_queue { struct phytmac_rx_buffer *rx_buffer_info; struct napi_struct rx_napi; struct phytmac_queue_stats stats; + struct xdp_rxq_info xdp_rxq; #ifdef CONFIG_PHYTMAC_ENABLE_PTP struct work_struct tx_ts_task; @@ -427,6 +450,7 @@ struct phytmac { struct platform_device *platdev; struct net_device *ndev; struct device *dev; + struct bpf_prog *xdp_prog; struct ncsi_dev *ncsidev; struct fwnode_handle *fwnode; struct phytmac_hw_if *hw_if; @@ -491,6 +515,23 @@ struct phytmac { u32 version; }; +/* phytmac_desc_unused - calculate if we have unused descriptors */ +static inline int phytmac_txdesc_unused(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (queue->tx_head > queue->tx_tail) + return queue->tx_head - queue->tx_tail - 1; + + return pdata->tx_ring_size + queue->tx_head - queue->tx_tail - 1; +} + +static inline struct netdev_queue *phytmac_get_txq(const struct phytmac *pdata, + struct phytmac_queue *queue) +{ + return netdev_get_tx_queue(pdata->ndev, queue->index); +} + struct phytmac_hw_if { int (*init_msg_ring)(struct phytmac *pdata); int (*init_hw)(struct phytmac *pdata); @@ -630,6 +671,7 @@ struct phytmac_hw_if { #define PHYTMAC_RX_DMA_ATTR \ (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) #define PHYTMAC_SKB_PAD (NET_SKB_PAD) +#define PHYTMAC_ETH_PKT_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) #define PHYTMAC_RXBUFFER_2048 2048 #define PHYTMAC_MAX_FRAME_BUILD_SKB \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 80d3495362ad48..26d70d9ccaef91 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include "phytmac.h" #include "phytmac_ptp.h" @@ -65,6 +67,16 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); * space in the SRAM (16KB) even when there is. */ +static int phytmac_xdp_xmit_back(struct phytmac *pdata, struct xdp_buff *xdp); + +static inline int phytmac_calc_rx_buf_len(void) +{ +#if (PAGE_SIZE < 8192) + return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); +#endif + return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); +} + static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_base_addr, int offset) { @@ -84,9 +96,20 @@ static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_ba static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) { + struct phytmac *pdata = netdev_priv(ndev); + int max_frame = new_mtu + PHYTMAC_ETH_PKT_HDR_PAD; + if (netif_running(ndev)) return -EBUSY; + if (pdata->xdp_prog) { + if (max_frame > phytmac_calc_rx_buf_len()) { + netdev_warn(pdata->ndev, + "Requested MTU size is not supported with XDP.\n"); + return -EINVAL; + } + } + if (new_mtu > MAX_MTU) { netdev_info(ndev, "Can not set MTU over %d.\n", MAX_MTU); return -EINVAL; @@ -301,14 +324,6 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) return nstat; } -static inline int phytmac_calc_rx_buf_len(void) -{ -#if (PAGE_SIZE < 8192) - return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); -#endif - return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); -} - inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, unsigned int index) { @@ -421,6 +436,11 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) if (queue->rx_ring) queue->rx_ring = NULL; + if (queue->xdp_prog) { + queue->xdp_prog = NULL; + xdp_rxq_info_unreg(&queue->xdp_rxq); + } + if (queue->rx_buffer_info) { vfree(queue->rx_buffer_info); queue->rx_buffer_info = NULL; @@ -548,6 +568,19 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) queue->rx_buffer_info = vzalloc(size); if (!queue->rx_buffer_info) goto err; + + memset(&queue->xdp_rxq, 0, sizeof(queue->xdp_rxq)); + WRITE_ONCE(queue->xdp_prog, pdata->xdp_prog); + + /* XDP RX-queue info */ + ret = xdp_rxq_info_reg(&queue->xdp_rxq, queue->pdata->ndev, q, 0); + if (ret < 0) { + netdev_err(pdata->ndev, "Failed to register xdp_rxq index %u\n", q); + goto err; + } + + xdp_rxq_info_unreg_mem_model(&queue->xdp_rxq); + WARN_ON(xdp_rxq_info_reg_mem_model(&queue->xdp_rxq, MEM_TYPE_PAGE_SHARED, NULL)); } return 0; @@ -863,6 +896,116 @@ static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, return skb; } +static void phytmac_rx_buffer_flip(struct phytmac_rx_buffer *rx_buffer, unsigned int size) +{ + unsigned int truesize; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; + rx_buffer->page_offset ^= truesize; +#else + truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); + rx_buffer->page_offset += truesize; +#endif +} + +static struct sk_buff *phytmac_run_xdp(struct phytmac *pdata, + struct xdp_buff *xdp) +{ + int err, result = PHYTMAC_XDP_PASS; + struct bpf_prog *xdp_prog; + u32 act; + + rcu_read_lock(); + xdp_prog = READ_ONCE(pdata->xdp_prog); + + if (!xdp_prog) + goto xdp_out; + + prefetchw(xdp->data_hard_start); /* xdp_frame write */ + + act = bpf_prog_run_xdp(xdp_prog, xdp); + switch (act) { + case XDP_PASS: + break; + case XDP_TX: + result = phytmac_xdp_xmit_back(pdata, xdp); + if (result == PHYTMAC_XDP_CONSUMED) + goto out_failure; + break; + case XDP_REDIRECT: + err = xdp_do_redirect(pdata->ndev, xdp, xdp_prog); + if (err) + goto out_failure; + result = PHYTMAC_XDP_REDIR; + break; + default: + bpf_warn_invalid_xdp_action(pdata->ndev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: +out_failure: + trace_xdp_exception(pdata->ndev, xdp_prog, act); + fallthrough; + case XDP_DROP: + result = PHYTMAC_XDP_CONSUMED; + break; + } +xdp_out: + rcu_read_unlock(); + return ERR_PTR(-result); +} + +static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, + struct phytmac_dma_desc *desc, + unsigned int *xdp_xmit) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_rx_buffer *rx_buffer; + struct sk_buff *skb = NULL; + unsigned int len; + struct xdp_buff xdp; + unsigned char *hard_start; + u32 frame_sz = 0; + + len = hw_if->get_rx_pkt_len(pdata, desc); + rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + + hw_if->zero_rx_desc_addr(desc); + +#if (PAGE_SIZE < 8192) + frame_sz = PHYTMAC_RX_PAGE_SIZE / 2; +#else + frame_sz = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + len); +#endif + xdp_init_buff(&xdp, frame_sz, &queue->xdp_rxq); + + hard_start = page_address(rx_buffer->page) + rx_buffer->page_offset - PHYTMAC_SKB_PAD; + xdp_prepare_buff(&xdp, hard_start, PHYTMAC_SKB_PAD, len, true); + xdp_buff_clear_frags_flag(&xdp); + skb = phytmac_run_xdp(pdata, &xdp); + + if (IS_ERR(skb)) { + unsigned int xdp_res = -PTR_ERR(skb); + + if (xdp_res & (PHYTMAC_XDP_TX | PHYTMAC_XDP_REDIR)) { + *xdp_xmit |= xdp_res; + phytmac_rx_buffer_flip(rx_buffer, len); + } else { + rx_buffer->pagecnt_bias++; + } + phytmac_put_rx_buffer(queue, rx_buffer); + pdata->ndev->stats.rx_bytes += len; + queue->stats.rx_bytes += len; + } else { + rx_buffer->pagecnt_bias++; + } + + return skb; +} + static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) { struct phytmac *pdata = queue->pdata; @@ -1048,6 +1191,7 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, struct phytmac_hw_if *hw_if = pdata->hw_if; struct sk_buff *skb; struct phytmac_dma_desc *desc; + unsigned int xdp_xmit = 0; int count = 0; while (count < budget) { @@ -1061,10 +1205,15 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, /* Ensure ctrl is at least as up-to-date as rxused */ dma_rmb(); - if (hw_if->rx_single_buffer(desc)) - skb = phytmac_rx_single(queue, desc); - else + if (hw_if->rx_single_buffer(desc)) { + skb = phytmac_rx_xdp_single(queue, desc, &xdp_xmit); + if (!IS_ERR(skb)) + skb = phytmac_rx_single(queue, desc); + } else { + if (pdata->xdp_prog) + netdev_warn(pdata->ndev, "xdp does not support multiple buffers!!\n"); skb = phytmac_rx_mbuffer(queue); + } if (!skb) { netdev_warn(pdata->ndev, "phytmac rx skb is NULL\n"); @@ -1073,18 +1222,28 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, pdata->ndev->stats.rx_packets++; queue->stats.rx_packets++; - pdata->ndev->stats.rx_bytes += skb->len; - queue->stats.rx_bytes += skb->len; + if (!IS_ERR(skb)) { + pdata->ndev->stats.rx_bytes += skb->len; + queue->stats.rx_bytes += skb->len; + } queue->rx_tail = (queue->rx_tail + 1) & (pdata->rx_ring_size - 1); count++; + if (IS_ERR(skb)) { + skb = NULL; + continue; + } + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) phytmac_ptp_rxstamp(pdata, skb, desc); napi_gro_receive(napi, skb); } + if (xdp_xmit & PHYTMAC_XDP_REDIR) + xdp_do_flush_map(); + phytmac_rx_clean(queue); return count; @@ -1102,8 +1261,13 @@ static void phytmac_tx_unmap(struct phytmac *pdata, struct phytmac_tx_skb *tx_sk tx_skb->addr = 0; } - if (tx_skb->skb) { - napi_consume_skb(tx_skb->skb, budget); + if (tx_skb->type == PHYTMAC_TYPE_XDP) { + if (tx_skb->xdpf) + xdp_return_frame(tx_skb->xdpf); + tx_skb->xdpf = NULL; + } else { + if (tx_skb->skb) + napi_consume_skb(tx_skb->skb, budget); tx_skb->skb = NULL; } } @@ -1137,6 +1301,19 @@ static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; } +static inline void phytmac_do_ptp_txstamp(struct phytmac_queue *queue, + struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + if (unlikely(skb_shinfo(skb)->tx_flags & + SKBTX_HW_TSTAMP) && + !phytmac_ptp_one_step(skb)) { + phytmac_ptp_txstamp(queue, skb, desc); + } + } +} + static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) { struct phytmac *pdata = queue->pdata; @@ -1163,27 +1340,32 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) /* Process all buffers of the current transmitted frame */ for (;; head++) { tx_skb = phytmac_get_tx_skb(queue, head); - skb = tx_skb->skb; - - if (skb) { - complete = 1; - if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { - if (unlikely(skb_shinfo(skb)->tx_flags & - SKBTX_HW_TSTAMP) && - !phytmac_ptp_one_step(skb)) { - phytmac_ptp_txstamp(queue, skb, desc); - } - } - - if (netif_msg_drv(pdata)) - netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", - head, tx_skb->skb->data); - pdata->ndev->stats.tx_packets++; - queue->stats.tx_packets++; - pdata->ndev->stats.tx_bytes += tx_skb->skb->len; - queue->stats.tx_bytes += tx_skb->skb->len; - packet_count++; + if (tx_skb->type == PHYTMAC_TYPE_SKB) { + skb = tx_skb->skb; + if (skb) { + complete = 1; + phytmac_do_ptp_txstamp(queue, skb, desc); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", + head, tx_skb->skb->data); + + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->skb->len; + queue->stats.tx_bytes += tx_skb->skb->len; + packet_count++; + } + } else if (tx_skb->type == PHYTMAC_TYPE_XDP) { + if (tx_skb->xdpf) { + complete = 1; + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->xdpf->len; + queue->stats.tx_bytes += tx_skb->xdpf->len; + packet_count++; + } } /* Now we can safely release resources */ @@ -1431,6 +1613,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, /* Save info to properly release resources */ tx_skb->skb = NULL; + tx_skb->type = PHYTMAC_TYPE_SKB; tx_skb->addr = mapping; tx_skb->length = size; tx_skb->mapped_as_page = false; @@ -1459,6 +1642,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, /* Save info to properly release resources */ tx_skb->skb = NULL; + tx_skb->type = PHYTMAC_TYPE_SKB; tx_skb->addr = mapping; tx_skb->length = size; tx_skb->mapped_as_page = true; @@ -1523,6 +1707,59 @@ static inline void phytmac_init_ring(struct phytmac *pdata) hw_if->init_ring_hw(pdata); } +static int phytmac_start_xmit_xdp(struct phytmac *pdata, + struct phytmac_queue *queue, + struct xdp_frame *xdpf) +{ + u32 len; + struct phytmac_tx_skb *tx_buffer; + struct packet_info packet; + dma_addr_t dma; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u16 tx_tail; + + len = xdpf->len; + + memset(&packet, 0, sizeof(struct packet_info)); + + if (unlikely(!phytmac_txdesc_unused(queue))) + return PHYTMAC_XDP_CONSUMED; + + dma = dma_map_single(pdata->dev, xdpf->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, dma)) + return PHYTMAC_XDP_CONSUMED; + + /* record the location of the first descriptor for this packet */ + tx_buffer = phytmac_get_tx_skb(queue, queue->tx_tail); + tx_buffer->mapped_as_page = false; + + /* Temporarily set the tail pointer for the next package */ + tx_tail = queue->tx_tail + 1; + + dma_unmap_len_set(tx_buffer, length, len); + dma_unmap_addr_set(tx_buffer, addr, dma); + tx_buffer->type = PHYTMAC_TYPE_XDP; + tx_buffer->xdpf = xdpf; + + packet.lso = 0; + packet.mss = 0; + packet.nocrc = 0; + + /* Avoid any potential race with xdp_xmit and cleanup */ + smp_wmb(); + + hw_if->tx_map(queue, tx_tail, &packet); + + queue->tx_tail = tx_tail & (pdata->tx_ring_size - 1); + + hw_if->transmit(queue); + + /* Make sure there is space in the ring for the next send. */ + phytmac_maybe_stop_tx_queue(queue, PHYTMAC_DESC_NEEDED); + + return PHYTMAC_XDP_TX; +} + static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct phytmac *pdata = netdev_priv(ndev); @@ -2187,6 +2424,140 @@ int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) return ret; } +static int phytmac_xdp_setup(struct net_device *dev, struct bpf_prog *prog) +{ + int i, frame_size = dev->mtu + PHYTMAC_ETH_PKT_HDR_PAD; + struct phytmac *pdata = netdev_priv(dev); + struct bpf_prog *old_prog; + bool running = netif_running(dev); + bool need_reset; + + /* verify phytmac rx ring attributes are sufficient for XDP */ + if (frame_size > phytmac_calc_rx_buf_len()) { + netdev_warn(dev, "XDP RX buffer size %d is too small for the frame size %d\n", + phytmac_calc_rx_buf_len(), frame_size); + return -EINVAL; + } + + old_prog = xchg(&pdata->xdp_prog, prog); + need_reset = (!!prog != !!old_prog); + + /* device is up and bpf is added/removed, must setup the RX queues */ + if (need_reset && running) { + phytmac_close(dev); + } else { + for (i = 0; i < pdata->queues_num; i++) + (void)xchg(&pdata->queues[i].xdp_prog, + pdata->xdp_prog); + } + + if (old_prog) + bpf_prog_put(old_prog); + + /* bpf is just replaced, RXQ and MTU are already setup */ + if (!need_reset) + return 0; + + if (prog) + xdp_features_set_redirect_target(dev, false); + else + xdp_features_clear_redirect_target(dev); + + if (running) + phytmac_open(dev); + + return 0; +} + +static int phytmac_xdp(struct net_device *dev, struct netdev_bpf *xdp) +{ + switch (xdp->command) { + case XDP_SETUP_PROG: + return phytmac_xdp_setup(dev, xdp->prog); + default: + return -EINVAL; + } +} + +static struct phytmac_queue *phytmac_xdp_txq_mapping(struct phytmac *pdata) +{ + unsigned int r_idx = smp_processor_id(); + + if (r_idx >= pdata->queues_num) + r_idx = r_idx % pdata->queues_num; + + return &pdata->queues[r_idx]; +} + +static int phytmac_xdp_xmit_back(struct phytmac *pdata, struct xdp_buff *xdp) +{ + struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp); + int cpu = smp_processor_id(); + struct phytmac_queue *queue; + struct netdev_queue *nq; + u32 ret; + + if (unlikely(!xdpf)) + return PHYTMAC_XDP_CONSUMED; + + /* During program transitions its possible adapter->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + queue = pdata->xdp_prog ? phytmac_xdp_txq_mapping(pdata) : NULL; + if (unlikely(!queue)) + return PHYTMAC_XDP_CONSUMED; + + nq = phytmac_get_txq(pdata, queue); + __netif_tx_lock(nq, cpu); + /* Avoid transmit queue timeout since we share it with the slow path */ + txq_trans_cond_update(nq); + ret = phytmac_start_xmit_xdp(pdata, queue, xdpf); + __netif_tx_unlock(nq); + + return ret; +} + +static int phytmac_xdp_xmit(struct net_device *dev, int n, + struct xdp_frame **frames, u32 flags) +{ + struct phytmac *pdata = netdev_priv(dev); + int cpu = smp_processor_id(); + struct phytmac_queue *queue; + struct netdev_queue *nq; + int nxmit = 0; + int i; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + /* During program transitions its possible pdata->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + queue = pdata->xdp_prog ? phytmac_xdp_txq_mapping(pdata) : NULL; + if (unlikely(!queue)) + return -ENXIO; + + nq = phytmac_get_txq(pdata, queue); + __netif_tx_lock(nq, cpu); + + /* Avoid transmit queue timeout since we share it with the slow path */ + txq_trans_cond_update(nq); + + for (i = 0; i < n; i++) { + struct xdp_frame *xdpf = frames[i]; + int err; + + err = phytmac_start_xmit_xdp(pdata, queue, xdpf); + if (err != PHYTMAC_XDP_TX) + break; + nxmit++; + } + + __netif_tx_unlock(nq); + + return nxmit; +} + static const struct net_device_ops phytmac_netdev_ops = { .ndo_open = phytmac_open, .ndo_stop = phytmac_close, @@ -2201,6 +2572,8 @@ static const struct net_device_ops phytmac_netdev_ops = { .ndo_features_check = phytmac_features_check, .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, + .ndo_bpf = phytmac_xdp, + .ndo_xdp_xmit = phytmac_xdp_xmit, }; static int phytmac_init(struct phytmac *pdata) @@ -2307,6 +2680,7 @@ void phytmac_default_config(struct phytmac *pdata) ndev->max_mtu = ETH_DATA_LEN; ndev->features = ndev->hw_features; + ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT; } static void phytmac_ncsi_handler(struct ncsi_dev *nd) From d9efe5157eaab87ba2a088859279eb29f5f4f7a7 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 337/521] PHYTIUM: net/phytmac: Clear RX descriptor address after the skb construction If the skb build failed, the descriptor address has been cleared, and the current descriptor will be considered invalid in the next round of processing. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I67900da07421ed3773e3f29dd2ddb16ce39fae1d Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/35be7e0f4fe7ed1612b14c56a83f497d06138afd Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_main.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 1d89c9bd023deb..5ea2a383c10742 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.43" +#define PHYTMAC_DRIVER_VERSION "1.0.44" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 26d70d9ccaef91..df0aea0de7a412 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -972,8 +972,6 @@ static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); - hw_if->zero_rx_desc_addr(desc); - #if (PAGE_SIZE < 8192) frame_sz = PHYTMAC_RX_PAGE_SIZE / 2; #else @@ -996,6 +994,7 @@ static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, } else { rx_buffer->pagecnt_bias++; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); pdata->ndev->stats.rx_bytes += len; queue->stats.rx_bytes += len; @@ -1016,7 +1015,6 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); - hw_if->zero_rx_desc_addr(desc); skb = phytmac_build_skb(rx_buffer, len); if (unlikely(!skb)) { @@ -1028,6 +1026,7 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy return NULL; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); skb->protocol = eth_type_trans(skb, pdata->ndev); @@ -1063,7 +1062,6 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, desc = phytmac_get_rx_desc(queue, first_frag); rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); - hw_if->zero_rx_desc_addr(desc); skb = phytmac_build_skb(rx_buffer, frag_len); if (unlikely(!skb)) { @@ -1074,6 +1072,7 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, return NULL; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); for (frag = first_frag + 1; ; frag++) { From 09dd9b67ac3606d68c1ee48d67acbe9de77bdfed Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 338/521] PHYTIUM: net/phytmac: Enable the tail pointer by the driver It is need to enable the RX/TX tail pointer to ensure the NIC works properly. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6e7fbf04484539add0834d0aaa0fee1cb0001355 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/e1f3cb62ca1bb3fc7267def60f2f853ad27d2280 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 8 ++++++++ drivers/net/ethernet/phytium/phytmac_v2.h | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 5ea2a383c10742..abe43324cd6aac 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.44" +#define PHYTMAC_DRIVER_VERSION "1.0.45" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 7b337d13329fa5..2e9d14dcb9f31c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -159,8 +159,16 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) u16 cmd_id, cmd_subid; struct phytmac_dma_info dma; struct phytmac_eth_info eth; + u32 ptrconfig = 0; u8 mdc; + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + ptrconfig |= PHYTMAC_BIT(TXTAIL_EN); + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + ptrconfig |= PHYTMAC_BIT(RXTAIL_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR_ENABLE, ptrconfig); + if (pdata->mii_bus) { cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 57594732fab989..1303bde7cff058 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -17,6 +17,7 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_RX_MSG_TAIL 0x00c #define PHYTMAC_MSG_IMR 0x020 #define PHYTMAC_MSG_ISR 0x02c +#define PHYTMAC_TAILPTR_ENABLE 0x038 #define PHYTMAC_SIZE 0x0048 #define PHYTMAC_NETWORK_STATUS 0x0240 @@ -43,6 +44,12 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_MSG_COMPLETE_INDEX 0 #define PHYTMAC_MSG_COMPLETE_WIDTH 1 +/* Bitfields in PHYTMAC_TAILPTR_ENABLE */ +#define PHYTMAC_TXTAIL_EN_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXTAIL_EN_WIDTH 1 +#define PHYTMAC_RXTAIL_EN_INDEX 16 /* Enable rx tail */ +#define PHYTMAC_RXTAIL_EN_WIDTH 1 + /* Bitfields in PHYTMAC_SIZE */ #define PHYTMAC_MEM_SIZE_INDEX 0 #define PHYTMAC_MEM_SIZE_WIDTH 4 From 9e376c30e675fafd0dd28548ca7d74b020dd7183 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 339/521] PHYTIUM: net/phytmac: Modify cmd processing function Change the para size in the msg struct from 64 to 56 and add mutux to avoid race. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I295f019f2d659c656816c88c42ea74caf3afa1f0 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/3b223d03776f8201d3f0487fe61a10b5cd6b1b90 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 6 +- .../net/ethernet/phytium/phytmac_ethtool.c | 4 +- drivers/net/ethernet/phytium/phytmac_v1.c | 6 +- drivers/net/ethernet/phytium/phytmac_v2.c | 170 +++++++++--------- drivers/net/ethernet/phytium/phytmac_v2.h | 24 ++- 5 files changed, 114 insertions(+), 96 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index abe43324cd6aac..e8a640de47a69b 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -75,7 +75,8 @@ #define PHYTMAC_TX 0x1 #define PHYTMAC_RX 0x2 -#define PHYTMAC_GREGS_LEN 16 +#define PHYTMAC_ETHTOOLD_REGS_LEN 64 +#define PHYTMAC_STATIS_REG_NUM 45 #define PHYTMAC_MTU_MIN_SIZE ETH_MIN_MTU @@ -429,7 +430,8 @@ struct phytmac_msg { u32 tx_msg_ring_size; u32 rx_msg_ring_size; u32 tx_msg_head; - u32 tx_msg_tail; + u32 tx_msg_wr_tail; + u32 tx_msg_rd_tail; u32 rx_msg_head; u32 rx_msg_tail; /*use msg_mutex to protect msg */ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 23c46bd91f2630..2c0bbb8f277203 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -70,7 +70,7 @@ static void phytmac_get_ethtool_strings(struct net_device *ndev, u32 sset, u8 *p static inline int phytmac_get_regs_len(struct net_device *ndev) { - return PHYTMAC_GREGS_LEN; + return PHYTMAC_ETHTOOLD_REGS_LEN; } static void phytmac_get_regs(struct net_device *ndev, @@ -81,7 +81,7 @@ static void phytmac_get_regs(struct net_device *ndev, struct phytmac_hw_if *hw_if = pdata->hw_if; u32 *regs_buff = p; - memset(p, 0, PHYTMAC_GREGS_LEN * sizeof(u32)); + memset(p, 0, PHYTMAC_ETHTOOLD_REGS_LEN); hw_if->get_regs(pdata, regs_buff); } diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index e7ac69753dd199..515825627fedc1 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1226,15 +1226,15 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) static void phytmac_get_hw_stats(struct phytmac *pdata) { - u32 stats[45]; + u32 stats[PHYTMAC_STATIS_REG_NUM]; int i, j; u64 val; u64 *p = &pdata->stats.tx_octets; - for (i = 0 ; i < 45; i++) + for (i = 0 ; i < PHYTMAC_STATIS_REG_NUM; i++) stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCTTX + i * 4); - for (i = 0, j = 0; i < 45; i++) { + for (i = 0, j = 0; i < PHYTMAC_STATIS_REG_NUM; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 2e9d14dcb9f31c..e43a55fe477004 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -20,44 +20,64 @@ #include "phytmac_v2.h" static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, - u16 cmd_subid, void *data, int len, int wait) + u16 cmd_subid, void *data, int len, int wait) { - int index = 0; + u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; int ret = 0; mutex_lock(&pdata->msg_ring.msg_mutex); - ++pdata->msg_ring.tx_msg_tail; - if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) - pdata->msg_ring.tx_msg_tail = 1; - index = pdata->msg_ring.tx_msg_tail; + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); + pdata->msg_ring.tx_msg_rd_tail = tx_tail; + ring_size = pdata->msg_ring.tx_msg_ring_size; + + while ((tx_tail + 1) % ring_size == tx_head) { + netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", + tx_tail, tx_head); + cpu_relax(); + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + } wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); msg.cmd_type = cmd_id; msg.cmd_subid = cmd_subid; - msg.status0 = PHYTMAC_FLAGS_MSG_NOINT; - - if (len) + if (len > 0 && len <= PHYTMAC_MSG_PARA_LEN) { memcpy(&msg.para[0], data, len); + } else if (len > PHYTMAC_MSG_PARA_LEN) { + netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", + len, PHYTMAC_MSG_PARA_LEN); + mutex_unlock(&pdata->msg_ring.msg_mutex); + return -EINVAL; + } if (netif_msg_hw(pdata)) { - netdev_info(pdata->ndev, "tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d\n", - msg.cmd_type, msg.cmd_subid, msg.status0, len, - pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "Tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d", + msg.cmd_type, msg.cmd_subid, msg.status0, len, tx_tail); } - memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); - PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, - pdata->msg_ring.tx_msg_tail | PHYTMAC_BIT(TX_MSG_INT)); + memcpy(pdata->msg_regs + PHYTMAC_MSG(tx_tail), &msg, sizeof(msg)); + tx_tail = phytmac_v2_tx_ring_wrap(pdata, ++tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, tx_tail | PHYTMAC_BIT(TX_MSG_INT)); + pdata->msg_ring.tx_msg_wr_tail = tx_tail; if (wait) { - memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); - while (!(msg_rx.status0 & PHYTMAC_CMD_PRC_COMPLETED)) { + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + while (tx_head != tx_tail) { cpu_relax(); - memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + } + + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(pdata->msg_ring.tx_msg_rd_tail), + PHYTMAC_MSG_HDR_LEN); + if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { + netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", + msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); + mutex_unlock(&pdata->msg_ring.msg_mutex); + return -EINVAL; } } @@ -105,10 +125,8 @@ static int phytmac_v2_get_mac_addr(struct phytmac *pdata, u8 *addr) cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, sizeof(struct phytmac_mac)); addr[0] = para.addrl & 0xff; @@ -149,7 +167,7 @@ static int phytmac_v2_pcs_software_reset(struct phytmac *pdata, int reset) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } @@ -225,7 +243,7 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; mdc = PHYTMAC_CLK_DIV96; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; @@ -302,25 +320,23 @@ static int phytmac_v2_init_ring_hw(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 1); return 0; } static int phytmac_v2_init_msg_ring(struct phytmac *pdata) { - u32 size = 0; - - pdata->msg_ring.tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; - size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); - pdata->msg_ring.tx_msg_ring_size = size; - if (pdata->msg_ring.tx_msg_tail == size) - pdata->msg_ring.tx_msg_tail = 0; + u32 tx_msg_tail; + pdata->msg_ring.tx_msg_ring_size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); + tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; + pdata->msg_ring.tx_msg_wr_tail = phytmac_v2_tx_ring_wrap(pdata, tx_msg_tail); + pdata->msg_ring.tx_msg_rd_tail = pdata->msg_ring.tx_msg_wr_tail; PHYTMAC_WRITE(pdata, PHYTMAC_MSG_IMR, 0xfffffffe); if (netif_msg_hw(pdata)) - netdev_info(pdata->ndev, "mac msg ring: tx_msg_ring_size=%d, tx_msg_tail=%d\n", - size, pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "Msg ring size:%d, tx msg tail=%d\n", + pdata->msg_ring.tx_msg_ring_size, tx_msg_tail); return 0; } @@ -335,12 +351,8 @@ static int phytmac_v2_get_feature_all(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, sizeof(struct phytmac_feature)); pdata->queues_max_num = para.queue_num; @@ -371,46 +383,38 @@ static void phytmac_v2_get_regs(struct phytmac *pdata, u32 *reg_buff) { u16 cmd_id, cmd_subid; int index; - u8 interface; + struct phytmac_ethtool_reg msg; + memset(&msg, 0, sizeof(msg)); cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL; - interface = pdata->phytmac_v2_interface; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&interface), sizeof(interface), 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, - READ_REG_NUM_MAX * sizeof(u32)); + msg.interface = pdata->phytmac_v2_interface; + /* There are 16 regs in total, read 14 regs at first time, read 2 regs at last time */ + msg.cnt = 0; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&msg), sizeof(msg), 1); + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, + PHYTMAC_MSG_PARA_LEN); + + msg.cnt = 1; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&msg), sizeof(msg), 1); + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(reg_buff + PHYTMAC_MSG_PARA_LEN / sizeof(u32), pdata->msg_regs + + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, + PHYTMAC_ETHTOOLD_REGS_LEN - PHYTMAC_MSG_PARA_LEN); } static void phytmac_v2_get_hw_stats(struct phytmac *pdata) { - u16 cmd_id, cmd_subid; - u8 count; - int i, j, index; - u32 stats[48]; + u32 stats[PHYTMAC_STATIS_REG_NUM]; + int i, j; u64 val; u64 *p = &pdata->stats.tx_octets; - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - /* There are 45 registers in total, read 16 regs at a time, read 13 regs at last time */ - for (i = 1; i <= 3; i++) { - count = i; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(&stats[(i - 1) * READ_REG_NUM_MAX], pdata->msg_regs + PHYTMAC_MSG(index) + - MSG_HDR_LEN, sizeof(u32) * READ_REG_NUM_MAX); - } + for (i = 0 ; i < PHYTMAC_STATIS_REG_NUM; i++) + stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCT_TX + i * 4); - for (i = 0, j = 0; i < 45; i++) { + for (i = 0, j = 0; i < PHYTMAC_STATIS_REG_NUM; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; @@ -459,7 +463,7 @@ static int phytmac_v2_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int } static int phytmac_v2_mdio_data_write_c22(struct phytmac *pdata, int mii_id, - int regnum, u16 data) + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_WRITE) @@ -496,7 +500,7 @@ static int phytmac_v2_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int } static int phytmac_v2_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, - int regnum, u16 data) + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) @@ -538,7 +542,7 @@ static int phytmac_v2_powerup_hw(struct phytmac *pdata, int on) handle = ACPI_HANDLE(pdata->dev); netdev_info(pdata->ndev, "set gmac power %s\n", - on == PHYTMAC_POWERON ? "on" : "off"); + on == PHYTMAC_POWERON ? "on" : "off"); args[0].type = ACPI_TYPE_INTEGER; args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; args[1].type = ACPI_TYPE_INTEGER; @@ -597,10 +601,10 @@ static int phytmac_v2_powerup_hw(struct phytmac *pdata, int on) rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } pdata->power_state = on; @@ -832,7 +836,7 @@ static u32 phytmac_v2_get_irq_status(u32 value) } static void phytmac_v2_enable_irq(struct phytmac *pdata, - int queue_index, u32 mask) + int queue_index, u32 mask) { u32 value; @@ -841,7 +845,7 @@ static void phytmac_v2_enable_irq(struct phytmac *pdata, } static void phytmac_v2_disable_irq(struct phytmac *pdata, - int queue_index, u32 mask) + int queue_index, u32 mask) { u32 value; @@ -850,7 +854,7 @@ static void phytmac_v2_disable_irq(struct phytmac *pdata, } static void phytmac_v2_clear_irq(struct phytmac *pdata, - int queue_index, u32 mask) + int queue_index, u32 mask) { u32 value; @@ -870,7 +874,7 @@ static unsigned int phytmac_v2_get_irq(struct phytmac *pdata, int queue_index) } static void phytmac_v2_interface_config(struct phytmac *pdata, unsigned int mode, - const struct phylink_link_state *state) + const struct phylink_link_state *state) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; @@ -900,7 +904,7 @@ static void phytmac_v2_interface_config(struct phytmac *pdata, unsigned int mode } static int phytmac_v2_interface_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) + int speed, int duplex) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; @@ -928,7 +932,7 @@ static int phytmac_v2_interface_linkdown(struct phytmac *pdata) } static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) + int speed, int duplex) { u16 cmd_id, cmd_subid; @@ -937,7 +941,7 @@ static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interfac cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); } return 0; @@ -962,7 +966,7 @@ static unsigned int phytmac_v2_pcs_get_link(struct phytmac *pdata, phy_interface } static unsigned int phytmac_v2_tx_map_desc(struct phytmac_queue *queue, - u32 tx_tail, struct packet_info *packet) + u32 tx_tail, struct packet_info *packet) { unsigned int i, ctrl; struct phytmac *pdata = queue->pdata; @@ -1006,7 +1010,7 @@ static unsigned int phytmac_v2_tx_map_desc(struct phytmac_queue *queue, } static void phytmac_v2_init_rx_map_desc(struct phytmac_queue *queue, - u32 index) + u32 index) { struct phytmac_dma_desc *desc; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 1303bde7cff058..4e195dcef04fe8 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -6,10 +6,11 @@ extern struct phytmac_hw_if phytmac_2p0_hw; -#define PHYTMAC_CMD_PRC_COMPLETED 0x1 +#define PHYTMAC_CMD_PRC_SUCCESS 0x1 #define PHYTMAC_MSG_SRAM_SIZE 4096 -#define MSG_HDR_LEN 8 -#define READ_REG_NUM_MAX 16 +#define PHYTMAC_MSG_HDR_LEN 8 +#define PHYTMAC_MSG_PARA_LEN 56 +#define PHYTMAC_READ_REG_NUM_MAX (PHYTMAC_MSG_PARA_LEN / sizeof(u32)) #define PHYTMAC_TX_MSG_HEAD 0x000 #define PHYTMAC_TX_MSG_TAIL 0x004 @@ -30,7 +31,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_TIMER_SEC 0x0258 #define PHYTMAC_TIMER_NSEC 0x025c #define PHYTMAC_TIMER_ADJUST 0x0260 -#define PHYTMAC_MSG(i) (((i) - 1) * 0x48) +#define PHYTMAC_MSG(i) ((i) * sizeof(struct phytmac_msg_info)) +#define PHYTMAC_OCT_TX 0x400 #define PHYTMAC_MODULE_ID_GMAC 0x60 #define PHYTMAC_FLAGS_MSG_COMP 0x1 @@ -255,7 +257,7 @@ enum phytmac_msg_cmd_id { }; enum phytmac_default_subid { - PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 0, + PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 1, PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE, PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE, }; @@ -415,7 +417,7 @@ struct phytmac_msg_info { u16 len; u8 status1; u8 status0; - u8 para[64]; + u8 para[PHYTMAC_MSG_PARA_LEN]; } __packed; struct phytmac_ots_config { @@ -424,4 +426,14 @@ struct phytmac_ots_config { u8 queuenum; } __packed; +struct phytmac_ethtool_reg { + u8 interface; + u8 cnt; +} __packed; + +static inline unsigned int phytmac_v2_tx_ring_wrap(struct phytmac *pdata, unsigned int index) +{ + return index & (pdata->msg_ring.tx_msg_ring_size - 1); +} + #endif From afacece960403fdd8b834685fca7342ecdac0190 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 340/521] PHYTIUM: net/phytmac: Bugfix set WOL failed issue Before configuring WOL, we need to obtain the packet types that WOL supports. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I22ed39f691044f35fa09ee76788711fc723fc6fc Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b44b37fdc57a32edb8bf0a5e763ea71c716cce09 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../net/ethernet/phytium/phytmac_ethtool.c | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 2c0bbb8f277203..bac59327087fc9 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -90,24 +90,20 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); + wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); - if (pdata->wol & PHYTMAC_WAKE_MAGIC) { + wol->supported = WAKE_MAGIC | WAKE_ARP | + WAKE_UCAST | WAKE_MCAST; + + if (pdata->wol & PHYTMAC_WAKE_MAGIC) wol->wolopts |= WAKE_MAGIC; - wol->supported |= WAKE_MAGIC; - } - if (pdata->wol & PHYTMAC_WAKE_ARP) { + if (pdata->wol & PHYTMAC_WAKE_ARP) wol->wolopts |= WAKE_ARP; - wol->supported |= WAKE_ARP; - } - if (pdata->wol & PHYTMAC_WAKE_UCAST) { + if (pdata->wol & PHYTMAC_WAKE_UCAST) wol->wolopts |= WAKE_UCAST; - wol->supported |= WAKE_UCAST; - } - if (pdata->wol & PHYTMAC_WAKE_MCAST) { + if (pdata->wol & PHYTMAC_WAKE_MCAST) wol->wolopts |= WAKE_MCAST; - wol->supported |= WAKE_MCAST; - } } static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) From 4722f729a69f2cbc612eec90f060bf60908544f6 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 341/521] PHYTIUM: net/phytmac: Bugfix invalid wait context After the spinlock is called, the mutex should no longer to be invoked, which will lead to unnecessary sleep. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I325188e12bdf0674454aba238624d05e4d877d38 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/d35f06971ac92e652e17033d40067c58e0cfd90c Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 6 ++---- drivers/net/ethernet/phytium/phytmac_main.c | 6 +----- drivers/net/ethernet/phytium/phytmac_v2.c | 10 ++++------ 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index e8a640de47a69b..7acfc97d3fc293 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -434,8 +434,8 @@ struct phytmac_msg { u32 tx_msg_rd_tail; u32 rx_msg_head; u32 rx_msg_tail; - /*use msg_mutex to protect msg */ - struct mutex msg_mutex; + /*use msg_lock to protect msg */ + spinlock_t msg_lock; }; struct ts_ctrl { @@ -473,8 +473,6 @@ struct phytmac { struct work_struct restart_task; /* Lock to protect mac config */ spinlock_t lock; - /* Lock to protect msg tx */ - spinlock_t msg_lock; u32 rx_ring_size; u32 tx_ring_size; u32 dma_data_width; diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index df0aea0de7a412..765b01503c1f40 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2725,8 +2725,6 @@ int phytmac_drv_probe(struct phytmac *pdata) if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); - mutex_init(&pdata->msg_ring.msg_mutex); - if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); if (ret) { @@ -2793,8 +2791,6 @@ int phytmac_drv_remove(struct phytmac *pdata) if (pdata->phylink) phylink_destroy(pdata->phylink); - - mutex_destroy(&pdata->msg_ring.msg_mutex); } return 0; @@ -2904,7 +2900,7 @@ struct phytmac *phytmac_alloc_pdata(struct device *dev) pdata->dev = dev; spin_lock_init(&pdata->lock); - spin_lock_init(&pdata->msg_lock); + spin_lock_init(&pdata->msg_ring.msg_lock); spin_lock_init(&pdata->ts_clk_lock); pdata->msg_enable = netif_msg_init(debug, PHYTMAC_DEFAULT_MSG_ENABLE); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index e43a55fe477004..65db2c0d15dea8 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -27,7 +27,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, struct phytmac_msg_info msg_rx; int ret = 0; - mutex_lock(&pdata->msg_ring.msg_mutex); + spin_lock(&pdata->msg_ring.msg_lock); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); pdata->msg_ring.tx_msg_rd_tail = tx_tail; @@ -36,7 +36,6 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, while ((tx_tail + 1) % ring_size == tx_head) { netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", tx_tail, tx_head); - cpu_relax(); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; } @@ -50,7 +49,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, } else if (len > PHYTMAC_MSG_PARA_LEN) { netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", len, PHYTMAC_MSG_PARA_LEN); - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return -EINVAL; } @@ -67,7 +66,6 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; while (tx_head != tx_tail) { - cpu_relax(); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; } @@ -76,12 +74,12 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return -EINVAL; } } - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return ret; } From b3bbde317d4e59aab9bdc872372a9c31b9974b4e Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 342/521] PHYTIUM: net/phytmac: Change the requested resource type from nRE to nRnE The resource type is changed from nRE to nRnE to avoid interrupt loss at low probability of occurrence. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If47dd823936b7c66b809952c844e2acff06a8ed1 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/cd72b3d4a536cd3f67210de692345ae704133d5f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 + drivers/net/ethernet/phytium/phytmac_main.c | 66 +++++++++++++++++++ .../net/ethernet/phytium/phytmac_platform.c | 3 +- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 7acfc97d3fc293..6b72b45bc0d4a7 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -680,6 +680,8 @@ struct phytmac_hw_if { #define PHYTMAC_RX_PAGE_ORDER 0 #define PHYTMAC_RX_PAGE_SIZE (PAGE_SIZE << PHYTMAC_RX_PAGE_ORDER) +void __iomem * +phytmac_devm_ioremap_resource_np(struct device *dev, const struct resource *res); struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index); inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 765b01503c1f40..c32dd959cca556 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2933,6 +2933,72 @@ void phytmac_drv_shutdown(struct phytmac *pdata) } EXPORT_SYMBOL_GPL(phytmac_drv_shutdown); +static void phytmac_devm_iounmap_np(struct device *dev, void *res) +{ + iounmap(*(void __iomem **)res); +} + +static void __iomem *phytmac_devm_ioremap_np(struct device *dev, resource_size_t offset, + resource_size_t size) +{ + void __iomem **ptr, *addr = NULL; + + ptr = devres_alloc_node(phytmac_devm_iounmap_np, sizeof(*ptr), GFP_KERNEL, + dev_to_node(dev)); + if (!ptr) + return NULL; + + addr = ioremap_np(offset, size); + if (addr) { + *ptr = addr; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return addr; +} + +void __iomem * +phytmac_devm_ioremap_resource_np(struct device *dev, const struct resource *res) +{ + resource_size_t size; + void __iomem *dest_ptr; + char *pretty_name; + + if (!res || resource_type(res) != IORESOURCE_MEM) { + dev_err(dev, "invalid resource %pR\n", res); + return IOMEM_ERR_PTR(-EINVAL); + } + + size = resource_size(res); + + if (res->name) + pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", + dev_name(dev), res->name); + else + pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!pretty_name) { + dev_err(dev, "can't generate pretty name for resource %pR\n", res); + return IOMEM_ERR_PTR(-ENOMEM); + } + + if (!devm_request_mem_region(dev, res->start, size, pretty_name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return IOMEM_ERR_PTR(-EBUSY); + } + + dest_ptr = phytmac_devm_ioremap_np(dev, res->start, size); + if (!dest_ptr) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + devm_release_mem_region(dev, res->start, size); + dest_ptr = IOMEM_ERR_PTR(-ENOMEM); + } + + return dest_ptr; +} +EXPORT_SYMBOL_GPL(phytmac_devm_ioremap_resource_np); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index eb3cc7d8f42906..540159f8fc3a83 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -203,7 +203,8 @@ static int phytmac_plat_probe(struct platform_device *pdev) } i = 0; - pdata->mac_regs = devm_platform_get_and_ioremap_resource(pdev, i, ®s); + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + pdata->mac_regs = phytmac_devm_ioremap_resource_np(&pdev->dev, regs); if (IS_ERR(pdata->mac_regs)) { dev_err(&pdev->dev, "mac_regs ioremap failed\n"); ret = PTR_ERR(pdata->mac_regs); From 7d42126530642e744b806d5ee0ddabc712778b6f Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 343/521] PHYTIUM: net/phytmac: BugFix Memory leak when releasing resource The size of the memory released in the dma_free_coherent is smaller than the size of the memory allocated in the dma_alloc_coherent, which will lead to memory leakage and fragmentation. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If06e83aec976cc485829b8e75d07e22ae7c6cfae Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/93ba71759d83f0122e818867a56bffcdc31f278d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 3 --- drivers/net/ethernet/phytium/phytmac_main.c | 12 ++++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 6b72b45bc0d4a7..09e3c99232d354 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -114,9 +114,6 @@ #define PHYTMAC_MSG_READ(_pdata, _reg) \ __raw_readl((_pdata)->mac_regs + (_reg)) -#define PHYTMAC_WRITE(_pdata, _reg, _val) \ - __raw_writel((_val), (_pdata)->mac_regs + (_reg)) - #define LSO_UFO 1 #define LSO_TSO 2 diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index c32dd959cca556..eeaf60a6e9cde1 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -391,6 +391,7 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) struct phytmac_dma_desc *tx_ring_base = NULL; dma_addr_t tx_ring_base_addr; unsigned int q; + int tx_offset; int size; queue = pdata->queues; @@ -408,8 +409,9 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) } if (tx_ring_base) { - size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + - RING_ADDR_INTERVAL); + tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_offset = ALIGN(tx_offset, 4096); + size = pdata->queues_num * tx_offset; dma_free_coherent(pdata->dev, size, tx_ring_base, tx_ring_base_addr); } @@ -422,6 +424,7 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) struct phytmac_dma_desc *rx_ring_base = NULL; dma_addr_t rx_ring_base_addr; unsigned int q; + int rx_offset; int size; queue = pdata->queues; @@ -448,8 +451,9 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) } if (rx_ring_base) { - size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + - RING_ADDR_INTERVAL); + rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_offset = ALIGN(rx_offset, 4096); + size = pdata->queues_num * rx_offset; dma_free_coherent(pdata->dev, size, rx_ring_base, rx_ring_base_addr); } From 2c8e9ba854ce814e794b42bf1534ce579dc360a1 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 344/521] PHYTIUM: net/phytmac: Limit the number of retries to avoid deadlock It is need to limit retries that messages are processed to avoid deadlock, when RV has no response. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ic300b8c8544af91c62564caf538a2a2eb6c0b512 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/d8fbda0b50a320673194e971fac55b6adbb2a85f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_v2.c | 20 ++++++++++++++++++-- drivers/net/ethernet/phytium/phytmac_v2.h | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 65db2c0d15dea8..20c49bf5f4b86e 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -25,6 +25,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; + u32 retry = 0; int ret = 0; spin_lock(&pdata->msg_ring.msg_lock); @@ -34,11 +35,19 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, ring_size = pdata->msg_ring.tx_msg_ring_size; while ((tx_tail + 1) % ring_size == tx_head) { - netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", - tx_tail, tx_head); + udelay(1); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + retry++; + if (retry >= PHYTMAC_RETRY_TIMES) { + netdev_err(pdata->ndev, + "Time out waiting for Tx msg ring free, tx_tail:0x%x, tx_head:0x%x", + tx_tail, tx_head); + spin_unlock(&pdata->msg_ring.msg_lock); + return -EINVAL; + } } + retry = 0; wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); @@ -66,7 +75,14 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; while (tx_head != tx_tail) { + udelay(1); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + retry++; + if (retry >= PHYTMAC_RETRY_TIMES) { + netdev_err(pdata->ndev, "Msg process time out!"); + spin_unlock(&pdata->msg_ring.msg_lock); + return -EINVAL; + } } memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(pdata->msg_ring.tx_msg_rd_tail), diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 4e195dcef04fe8..b32044d696ae2d 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -246,6 +246,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_CLK_DIV128 6 #define PHYTMAC_CLK_DIV224 7 +#define PHYTMAC_RETRY_TIMES 50000 + #define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) enum phytmac_msg_cmd_id { From dfb48e403c678a90d16829d6097423053d2530ee Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 30 May 2025 11:41:46 +0800 Subject: [PATCH 345/521] PHYTIUM: net: phytmac: update to 1.0.47 Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f12de776aa34dbdc7e389542ade8a67325d18925 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 8 +++++--- drivers/net/ethernet/phytium/phytmac_ethtool.c | 9 ++++++--- drivers/net/ethernet/phytium/phytmac_main.c | 17 ++++++++++++++--- drivers/net/ethernet/phytium/phytmac_pci.c | 4 ++-- drivers/net/ethernet/phytium/phytmac_platform.c | 2 +- drivers/net/ethernet/phytium/phytmac_v1.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.h | 2 +- 8 files changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 09e3c99232d354..ee22cda030e94a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -14,9 +14,10 @@ #include #include -#define PHYTMAC_DRV_NAME "phytium-mac" +#define PHYTMAC_PCI_DRV_NAME "phytmac_pci" +#define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.45" +#define PHYTMAC_DRIVER_VERSION "1.0.47" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -54,7 +55,7 @@ #define DEFAULT_MSG_RING_SIZE 16 -#define PHYTMAC_MDIO_TIMEOUT 1000000 /* in usecs */ +#define PHYTMAC_MDIO_TIMEOUT 1000000 /* in usecs */ #define PHYTMAC_CAPS_JUMBO 0x00000001 #define PHYTMAC_CAPS_PTP 0x00000002 @@ -510,6 +511,7 @@ struct phytmac { spinlock_t rx_fs_lock; unsigned int max_rx_fs; u32 version; + char fw_version[32]; }; /* phytmac_desc_unused - calculate if we have unused descriptors */ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index bac59327087fc9..26478b090ced0d 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -506,13 +506,16 @@ static void phytmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo { struct phytmac *pdata = netdev_priv(ndev); - strscpy(drvinfo->driver, PHYTMAC_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->version, PHYTMAC_DRIVER_VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->fw_version, pdata->fw_version, sizeof(drvinfo->fw_version)); - if (pdata->platdev) + if (pdata->platdev) { + strscpy(drvinfo->driver, PHYTMAC_PLAT_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->bus_info, pdata->platdev->name, sizeof(drvinfo->bus_info)); - else if (pdata->pcidev) + } else if (pdata->pcidev) { + strscpy(drvinfo->driver, PHYTMAC_PCI_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->bus_info, pci_name(pdata->pcidev), sizeof(drvinfo->bus_info)); + } } static const struct ethtool_ops phytmac_ethtool_ops = { diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index eeaf60a6e9cde1..9ed5ec09523b3e 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -62,7 +62,7 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); /* Max length of transmit frame must be a multiple of 8 bytes */ #define PHYTMAC_TX_LEN_ALIGN 8 -/* Limit maximum TX length as per Cadence TSO errata. This is to avoid a +/* Limit maximum TX length as per TSO errata. This is to avoid a * false amba_error in TX path from the DMA assuming there is not enough * space in the SRAM (16KB) even when there is. */ @@ -584,7 +584,8 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) } xdp_rxq_info_unreg_mem_model(&queue->xdp_rxq); - WARN_ON(xdp_rxq_info_reg_mem_model(&queue->xdp_rxq, MEM_TYPE_PAGE_SHARED, NULL)); + WARN_ON(xdp_rxq_info_reg_mem_model(&queue->xdp_rxq, + MEM_TYPE_PAGE_SHARED, NULL)); } return 0; @@ -2684,6 +2685,16 @@ void phytmac_default_config(struct phytmac *pdata) ndev->features = ndev->hw_features; ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT; + + switch (pdata->version) { + case VERSION_V3: + strscpy(pdata->fw_version, "MAC_FTM300", sizeof(pdata->fw_version)); + break; + + default: + strscpy(pdata->fw_version, "", sizeof(pdata->fw_version)); + break; + } } static void phytmac_ncsi_handler(struct ncsi_dev *nd) @@ -2835,8 +2846,8 @@ int phytmac_drv_suspend(struct phytmac *pdata) rtnl_unlock(); spin_lock_irqsave(&pdata->lock, flags); hw_if->reset_hw(pdata); - hw_if->poweron(pdata, PHYTMAC_POWEROFF); spin_unlock_irqrestore(&pdata->lock, flags); + hw_if->poweron(pdata, PHYTMAC_POWEROFF); } return 0; diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index 62766c49d1a626..fac5d717ad7669 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -74,7 +74,7 @@ static int phytmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i /* Obtain the mmio areas for the device */ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); - ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_DRV_NAME); + ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_PCI_DRV_NAME); if (ret) { dev_err(dev, "pcim_iomap_regions failed\n"); goto err_pci_enable; @@ -305,7 +305,7 @@ static const struct dev_pm_ops phytmac_pci_pm_ops = { }; static struct pci_driver phytmac_driver = { - .name = PHYTMAC_DRV_NAME, + .name = PHYTMAC_PCI_DRV_NAME, .id_table = phytmac_pci_table, .probe = phytmac_pci_probe, .remove = phytmac_pci_remove, diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 540159f8fc3a83..2f2170cf0775ae 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -356,7 +356,7 @@ static struct platform_driver phytmac_driver = { .probe = phytmac_plat_probe, .remove = phytmac_plat_remove, .driver = { - .name = PHYTMAC_DRV_NAME, + .name = PHYTMAC_PLAT_DRV_NAME, .of_match_table = of_match_ptr(phytmac_dt_ids), .acpi_match_table = phytmac_acpi_ids, .pm = &phytmac_plat_pm_ops, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 515825627fedc1..870f07edc58fc5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -564,7 +564,7 @@ static int phytmac_mdio_idle(struct phytmac *pdata) int ret; /* wait for end of transfer */ - ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + ret = readx_poll_timeout(PHYTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(MDIO_IDLE), 1, PHYTMAC_MDIO_TIMEOUT); if (ret) netdev_err(pdata->ndev, "mdio wait for idle time out!"); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 20c49bf5f4b86e..1afff67d625d99 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -453,7 +453,7 @@ static int phytmac_v2_mdio_idle(struct phytmac *pdata) int ret; /* wait for end of transfer */ - ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + ret = readx_poll_timeout(PHYTMAC_READ_NSR, pdata, val, val & PHYTMAC_BIT(MIDLE), 1, PHYTMAC_MDIO_TIMEOUT); if (ret) netdev_err(pdata->ndev, "mdio wait for idle time out!"); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index b32044d696ae2d..c9e159b1eb9b64 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -248,7 +248,7 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_RETRY_TIMES 50000 -#define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) +#define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, From 733533580105533c27761f4b1d08831cce6eea96 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Thu, 26 Jun 2025 14:43:04 +0800 Subject: [PATCH 346/521] PHYTIUM: net: phytmac: fix page refcounting for XDP_REDIRECT The current page counting scheme assumes that the reference count cannot decrease until the received frame is sent to the upper layers of the networking stack. This assumption does not hold for the XDP_REDIRECT action, particularly with BPF_MAP_TYPE_XSKMAP handling, since a page (pointed out by xdp_buff) can have its reference count decreased via the xdp_do_redirect call. To work around that, now start off by a large page count and then don't allow a refcount less than two. Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/ecb37f4f54397f2d41013318df9a5fde759da478 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 9ed5ec09523b3e..ece14f0a7b47eb 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -739,7 +739,8 @@ static bool phytmac_alloc_mapped_page(struct phytmac *pdata, bi->addr = dma; bi->page = page; bi->page_offset = PHYTMAC_SKB_PAD; - bi->pagecnt_bias = 1; + page_ref_add(page, USHRT_MAX - 1); + bi->pagecnt_bias = USHRT_MAX; return true; } @@ -769,8 +770,8 @@ static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) * the pagecnt_bias and page count so that we fully restock the * number of references the driver holds. */ - if (unlikely(!pagecnt_bias)) { - page_ref_add(page, USHRT_MAX); + if (unlikely(pagecnt_bias == 1)) { + page_ref_add(page, USHRT_MAX - 1); rx_buffer->pagecnt_bias = USHRT_MAX; } From b88d4c2755ef6d812aec356f7764396f23132dcb Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 18 Jul 2025 15:57:40 +0800 Subject: [PATCH 347/521] PHYTIUM: net/phytmac: Fix compile error on X86 architecture Add dependency on ARCH_PHYTIUM to slove the compilation problem of hybrid architecture, such as X86 and RISCV. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie2d1bafaa559ece975d0a892cc758ff210776efd Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/fc38c345dd09bec8628c897823a62428200bfea5 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/Kconfig | 1 + drivers/net/ethernet/phytium/phytmac.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index 14a77adbfdc12f..e68ca5d82181e8 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -5,6 +5,7 @@ config NET_VENDOR_PHYTIUM bool "Phytium devices" + depends on ARCH_PHYTIUM depends on HAS_IOMEM default y help diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index ee22cda030e94a..7d1ecbacf9c1a9 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -17,7 +17,7 @@ #define PHYTMAC_PCI_DRV_NAME "phytmac_pci" #define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.47" +#define PHYTMAC_DRIVER_VERSION "1.0.48" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ From 1acf6a4eb91103ba62dd3a8ba84035aa921426d5 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 18 Jul 2025 16:07:06 +0800 Subject: [PATCH 348/521] PHYTIUM: net/phytmac: Fix page refcounting issue for XDP_REDIRECT The current page counting scheme assumes that the reference count cannot decrease until the received frame is sent to the upper layers of the networking stack. This assumption does not hold for the XDP_REDIRECT action, particularly with BPF_MAP_TYPE_XSKMAP handling, since a page that pointed out by xdp_buff can have its reference count decreased via the xdp_do_redirect call. To work around that, now start off by a large page count and then don't allow a refcount less than two. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ieea455dabd7c5f4827b560bf82bd81ba6bd18784 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/71bc411dd6878806fe5590276b04ea8f9c9741ae Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 7d1ecbacf9c1a9..f483524b395016 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -17,7 +17,7 @@ #define PHYTMAC_PCI_DRV_NAME "phytmac_pci" #define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.48" +#define PHYTMAC_DRIVER_VERSION "1.0.49" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ From 0aec6ba22ae745a12d21e0d5838264605bf31931 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 18 Jul 2025 16:14:52 +0800 Subject: [PATCH 349/521] PHYTIUM: net/phytmac: Bugfix the potential deadlock issue If the SOFTIRQ-unsafe lock is called inside the SOFTIRQ-safe lock, it will cause high potential deadlock risk. In dependency chain &(&mc->mca_lock)->rlock --> &dev->addr_list_lock_key --> &(&pdata->msg_ring.msg_lock)->rlock, msg_ring.msg_lock use spin_lock, which is SOFTIRQ-unsafe. it should be modified to use spin_lock_irqsave. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I08e5787cd8537414ef132004d21d6a4504738304 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/39f4e319cea6eb80aab0e997335db58d2f21d381 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index f483524b395016..c3feca3d9611a0 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -17,7 +17,7 @@ #define PHYTMAC_PCI_DRV_NAME "phytmac_pci" #define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.49" +#define PHYTMAC_DRIVER_VERSION "1.0.50" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 1afff67d625d99..4dd3ddd18e7144 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -25,10 +25,11 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; + unsigned long flags; u32 retry = 0; int ret = 0; - spin_lock(&pdata->msg_ring.msg_lock); + spin_lock_irqsave(&pdata->msg_ring.msg_lock, flags); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); pdata->msg_ring.tx_msg_rd_tail = tx_tail; @@ -42,8 +43,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, netdev_err(pdata->ndev, "Time out waiting for Tx msg ring free, tx_tail:0x%x, tx_head:0x%x", tx_tail, tx_head); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } @@ -58,8 +59,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, } else if (len > PHYTMAC_MSG_PARA_LEN) { netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", len, PHYTMAC_MSG_PARA_LEN); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } if (netif_msg_hw(pdata)) { @@ -80,8 +81,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, retry++; if (retry >= PHYTMAC_RETRY_TIMES) { netdev_err(pdata->ndev, "Msg process time out!"); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } @@ -90,12 +91,13 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } - spin_unlock(&pdata->msg_ring.msg_lock); +err_out: + spin_unlock_irqrestore(&pdata->msg_ring.msg_lock, flags); return ret; } From e5c57a7eccea215b5cd85ddd9af4fad41d47a48f Mon Sep 17 00:00:00 2001 From: zuoqian Date: Sun, 28 Sep 2025 14:38:21 +0800 Subject: [PATCH 350/521] PHYTIUM: net: phytmac: fix fixed-link link down ethtool status Add reset pdata->speed & duplex when not link in fixed-link mode. When link is down, ethtool will show: ... Speed: Unknown! Duplex: Unknown! (255) ... rather than the old speed & duplex info at link up. Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/d93fa9c65b76e648488778d48eb205a47bcd04e3 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 26478b090ced0d..cd9d702da113ff 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -384,8 +384,13 @@ static int phytmac_get_link_ksettings(struct net_device *ndev, if (!ndev->phydev) { kset->base.port = PORT_FIBRE; kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = pdata->duplex; - kset->base.speed = pdata->speed; + if (netif_carrier_ok(ndev)) { + kset->base.duplex = pdata->duplex; + kset->base.speed = pdata->speed; + } else { + kset->base.duplex = DUPLEX_UNKNOWN; + kset->base.speed = SPEED_UNKNOWN; + } if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { From 3c8829a57fb0c6b8bf034ad49d4deba1b394a213 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Thu, 16 Oct 2025 18:05:41 +0800 Subject: [PATCH 351/521] PHYTIUM: net/phytmac: update mdc early to fix PHY ID read issue The MDC modification in the driver was previously positioned after mdiobus_register, which caused the MDC not to be properly configured when reading the PHY ID. This resulted in potential issues during PHY ID detection. This commit adjusts the driver by moving the MDC modification logic to before the call to mdiobus_register, ensuring the MDC is correctly set up prior to PHY ID reading operations. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/7c9ca9af7d15d02f20347c3030f179881f3eb596 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_v2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 4dd3ddd18e7144..0df1771ca3972f 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -194,7 +194,6 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) struct phytmac_dma_info dma; struct phytmac_eth_info eth; u32 ptrconfig = 0; - u8 mdc; if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) ptrconfig |= PHYTMAC_BIT(TXTAIL_EN); @@ -257,10 +256,6 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) dma.hw_dma_cap |= HW_DMA_CAP_DDW128; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); - cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; - mdc = PHYTMAC_CLK_DIV96; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); - memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; eth.index = 0; @@ -705,8 +700,13 @@ static int phytmac_v2_enable_txcsum(struct phytmac *pdata, int enable) static int phytmac_v2_enable_mdio(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; + u8 mdc; cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; + mdc = PHYTMAC_CLK_DIV96; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); + if (enable) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; else From cbbe855c7f13aba5b6cf1752aec09793e5f45113 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Thu, 16 Oct 2025 17:26:01 +0800 Subject: [PATCH 352/521] PHYTIUM: net: phytmac: support ipv6 TSO Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/9bc24c634575287721a14bbd1aa0944dd17d9bfe Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index ece14f0a7b47eb..ccdb2be4ab0bb2 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2365,7 +2365,7 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, hdrlen = skb_transport_offset(skb); if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, PHYTMAC_TX_LEN_ALIGN)) - return features & ~NETIF_F_TSO; + return features & ~(NETIF_F_TSO | NETIF_F_TSO6); nr_frags = skb_shinfo(skb)->nr_frags; /* No need to check last fragment */ @@ -2374,7 +2374,7 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; if (!IS_ALIGNED(skb_frag_size(frag), PHYTMAC_TX_LEN_ALIGN)) - return features & ~NETIF_F_TSO; + return features & ~(NETIF_F_TSO | NETIF_F_TSO6); } return features; } @@ -2664,7 +2664,7 @@ void phytmac_default_config(struct phytmac *pdata) ndev->hw_features = NETIF_F_SG; if (pdata->capacities & PHYTMAC_CAPS_LSO) - ndev->hw_features |= NETIF_F_TSO; + ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; if (pdata->use_ncsi) { ndev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); From dbd1fd47359c4d7d4fade55ead2371754031150b Mon Sep 17 00:00:00 2001 From: zuoqian Date: Fri, 24 Oct 2025 18:14:25 +0800 Subject: [PATCH 353/521] PHYTIUM: net: phytmac: resolve warnings when using jumbo frames Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b63c60849bffdf07de18d048f39b64323186d51e Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index ccdb2be4ab0bb2..1f4e9a2b97ee4f 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1131,7 +1131,7 @@ static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) for (rx_tail = queue->rx_tail; ; rx_tail++) { desc = phytmac_get_rx_desc(queue, rx_tail); if (!hw_if->rx_complete(desc)) - return NULL; + return ERR_PTR(-EAGAIN); if (hw_if->rx_pkt_start(desc)) { if (first_frag != -1) @@ -1218,6 +1218,9 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, if (pdata->xdp_prog) netdev_warn(pdata->ndev, "xdp does not support multiple buffers!!\n"); skb = phytmac_rx_mbuffer(queue); + if (PTR_ERR(skb) == -EAGAIN) { + break; + } } if (!skb) { From 69d19f54cfdae10199b5e4821cf08e6c79092b04 Mon Sep 17 00:00:00 2001 From: YunQiang Su Date: Mon, 16 Nov 2020 09:11:00 +0800 Subject: [PATCH 354/521] DEBIAN: Use RELAXED ieee754 mode for Loongson-3 as 3A 4000 is 2008-only There are 2 mode of value of IEEE NaN hardcoded by CPU. Currently, our mipsel/mips64el port is in so-called lagacy mode. Loongson 3A 4000 is set as the so-called 2008 mode. To make Debian workable on Loongson 3A 4000, we need set the kerenl in RELAXED mode. https://web.archive.org/web/20180830093617/https://dmz-portal.mips.com/wiki/MIPS_ABI_-_NaN_Interlinking [bwh: Update for addition of EMULATED mode in 6.11] Signed-off-by: YunQiang Su Link: https://salsa.debian.org/kernel-team/linux/-/blob/a71628c034f0c156c0868f53b14a9928c4d02f91/debian/patches/debian/mips-ieee754-relaxed.patch Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/fpu-probe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/mips/kernel/fpu-probe.c b/arch/mips/kernel/fpu-probe.c index 6bf3f19b1c335d..404d7531205824 100644 --- a/arch/mips/kernel/fpu-probe.c +++ b/arch/mips/kernel/fpu-probe.c @@ -144,7 +144,12 @@ static void cpu_set_fpu_2008(struct cpuinfo_mips *c) * IEEE 754 conformance mode to use. Affects the NaN encoding and the * ABS.fmt/NEG.fmt execution mode. */ -static enum { STRICT, EMULATED, LEGACY, STD2008, RELAXED } ieee754 = STRICT; +enum ieee754_mode { STRICT, EMULATED, LEGACY, STD2008, RELAXED }; +#ifdef CONFIG_CPU_LOONGSON64 +static enum ieee754_mode ieee754 = RELAXED; +#else +static enum ieee754_mode ieee754 = STRICT; +#endif /* * Set the IEEE 754 NaN encodings and the ABS.fmt/NEG.fmt execution modes From 724f7af126ae1833242ae1bd88b1599a2a317b3c Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:57:01 +0800 Subject: [PATCH 355/521] UBUNTU: ODM: hwmon: add driver for AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This refator patch adds support for the hwmon information which are transported to userspace through ASUS WMI interface. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/d927fbdbccb2b2d8f6891b6f51e1570e74e25e14 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/hwmon-aaeon.c | 568 ++++++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 drivers/hwmon/hwmon-aaeon.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 14e4cea48acc47..cc06e212d21a69 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -38,6 +38,18 @@ config HWMON_DEBUG_CHIP comment "Native drivers" +config SENSORS_AAEON + tristate "AAEON hwmon driver" + depends on X86 + depends on UBUNTU_ODM_DRIVERS + select MFD_AAEON + help + This hwmon driver adds support for reporting temperature or fan + speed and voltage on Single Board Computers produced by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + config SENSORS_ABITUGURU tristate "Abit uGuru (rev 1 & 2)" depends on (X86 && DMI) || COMPILE_TEST && HAS_IOPORT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 982ee2c6f9deb6..3a8bd0f9e64f29 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_HP_WMI) += hp-wmi-sensors.o # Native drivers # asb100, then w83781d go first, as they can override other drivers' addresses. +obj-$(CONFIG_SENSORS_AAEON) += hwmon-aaeon.o obj-$(CONFIG_SENSORS_ASB100) += asb100.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83773G) += w83773g.o diff --git a/drivers/hwmon/hwmon-aaeon.c b/drivers/hwmon/hwmon-aaeon.c new file mode 100644 index 00000000000000..146da10309bbb9 --- /dev/null +++ b/drivers/hwmon/hwmon-aaeon.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AAEON HWMON driver + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Edward Lin + * Author: Kunyang Fan + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "hwmon-aaeon" + +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +#define AAEON_VERSION_METHOD_ID 0x00000000 +#define HWM_INFORMATION_METHOD_ID 0x00030000 +#define HWM_METHOD_ID 0x00030001 + +#define BITMAP_TEMP_ARG 0x12 +#define BITMAP_FAN_ARG 0x13 +#define BITMAP_VOLTAGE_ARG 0x14 + +#define SENSOR_TEMP_NUMBER 0 +#define SENSOR_FAN_NUMBER 1 +#define SENSOR_VOLTAGE_NUMBER 2 +#define SENSOR_MAX_NUMBER 2 + +static ssize_t aaeon_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t aaeon_show_sensor_name(struct device *dev, + struct device_attribute *devattr, + char *buf); +static ssize_t aaeon_show_version(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t name_show(struct device *dev, struct device_attribute *devattr, + char *buf); +static int aaeon_get_version(void); +static int aaeon_hwmon_probe(struct platform_device *pdev); +static int aaeon_hwmon_remove(struct platform_device *pdev); + +static const char * const temp_sensors_name_table[] = { + "CPU_Temp", + "SYS1_Temp", + "SYS2_Temp", +}; + +static const char * const temp_sensors_name_table_V3[] = { + "SYS_Temp", + "CPU_Temp", +}; + +static const char * const fan_sensors_name_table[] = { + "CPU_FAN", + "SYS1_FAN", + "SYS2_FAN", + "Chasis1_FAN", + "Chasis2_FAN", +}; + +static const char * const fan_sensors_name_table_V3[] = { + "Chasis_FAN", + "CPU_FAN", +}; + +static const char * const voltage_sensors_name_table[] = { + "VCORE_Voltage", + "VMEM_Voltage", + "+12_Voltage", + "+5_Voltage", + "+3.3_Voltage", + "+1.8_Voltage", + "5VSB_Voltage", + "3VSB_Voltage", + "VBAT_Voltage", +}; + +static const char * const voltage_sensors_name_table_V3[] = { + "VCORE_Voltage", + "+5_Voltage", + "AVCC_Voltage", + "+3.3_Voltage", + "+12_Voltage", + "VCOREREFIN_Voltage", + "VIN4_Voltage", + "3VSB_Voltage", + "VBAT_Voltage", +}; + +struct aaeon_hwmon_data { + struct device *hwmon_dev; + int bfpi_version; + u32 temp_bitmap; + u32 fan_bitmap; + u32 voltage_bitmap; + unsigned int sensors_number[SENSOR_MAX_NUMBER + 1]; + const char * const *temp_names; + const char * const *fan_names; + const char * const *voltage_names; +}; + +/* Temperature attributes */ +static struct sensor_device_attribute_2 temp_sys_nodes_atts[] = { + SENSOR_ATTR_2(temp1_input, 0444, aaeon_show_sensor, NULL, + SENSOR_TEMP_NUMBER, 0), + SENSOR_ATTR_2(temp1_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_TEMP_NUMBER, 0), + SENSOR_ATTR_2(temp2_input, 0444, aaeon_show_sensor, NULL, + SENSOR_TEMP_NUMBER, 1), + SENSOR_ATTR_2(temp2_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_TEMP_NUMBER, 1), + SENSOR_ATTR_2(temp3_input, 0444, aaeon_show_sensor, NULL, + SENSOR_TEMP_NUMBER, 2), + SENSOR_ATTR_2(temp3_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_TEMP_NUMBER, 2), +}; + +/* Cooler Fan attributes */ +static struct sensor_device_attribute_2 fan_sys_nodes_atts[] = { + SENSOR_ATTR_2(fan1_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 0), + SENSOR_ATTR_2(fan1_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 0), + SENSOR_ATTR_2(fan2_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 1), + SENSOR_ATTR_2(fan2_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 1), + SENSOR_ATTR_2(fan3_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 2), + SENSOR_ATTR_2(fan3_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 2), + SENSOR_ATTR_2(fan4_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 3), + SENSOR_ATTR_2(fan4_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 3), + SENSOR_ATTR_2(fan5_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 4), + SENSOR_ATTR_2(fan5_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 4), +}; + +/* Voltage attributes */ +static struct sensor_device_attribute_2 voltage_sys_nodes_atts[] = { + SENSOR_ATTR_2(in1_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 0), + SENSOR_ATTR_2(in1_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 0), + SENSOR_ATTR_2(in2_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 1), + SENSOR_ATTR_2(in2_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 1), + SENSOR_ATTR_2(in3_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 2), + SENSOR_ATTR_2(in3_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 2), + SENSOR_ATTR_2(in4_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 3), + SENSOR_ATTR_2(in4_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 3), + SENSOR_ATTR_2(in5_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 4), + SENSOR_ATTR_2(in5_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 4), + SENSOR_ATTR_2(in6_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 5), + SENSOR_ATTR_2(in6_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 5), + SENSOR_ATTR_2(in7_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 6), + SENSOR_ATTR_2(in7_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 6), + SENSOR_ATTR_2(in8_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 7), + SENSOR_ATTR_2(in8_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 7), + SENSOR_ATTR_2(in9_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 8), + SENSOR_ATTR_2(in9_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 8), + +}; + +static struct sensor_device_attribute_2 info_sys_nodes_atts[] = { + /* WMI version Information */ + SENSOR_ATTR_2(AAEON_VERSION, 0444, aaeon_show_version, NULL, 0, 0), +}; + +DEVICE_ATTR_RO(name); +static ssize_t name_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "%s\n", DRVNAME); +} + +static ssize_t aaeon_show_version(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct aaeon_hwmon_data *data = + (struct aaeon_hwmon_data *)dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", data->bfpi_version); +} + +static ssize_t aaeon_show_sensor_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u8 nr = to_sensor_dev_attr_2(devattr)->nr; + u8 index = to_sensor_dev_attr_2(devattr)->index; + struct aaeon_hwmon_data *data = + (struct aaeon_hwmon_data *)dev_get_drvdata(dev); + + if (nr > SENSOR_MAX_NUMBER || index >= data->sensors_number[nr]) { + pr_debug("Can not check the device"); + return -1; + } + + switch (nr) { + case SENSOR_TEMP_NUMBER: + return sprintf(buf, "%s\n", data->temp_names[index]); + case SENSOR_FAN_NUMBER: + return sprintf(buf, "%s\n", data->fan_names[index]); + case SENSOR_VOLTAGE_NUMBER: + return sprintf(buf, "%s\n", data->voltage_names[index]); + default: + break; + } + + return 0; +} + +static ssize_t aaeon_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u8 nr = to_sensor_dev_attr_2(devattr)->nr; + u8 index = to_sensor_dev_attr_2(devattr)->index; + u32 dev_id; + int retval, err; + struct aaeon_hwmon_data *data = + (struct aaeon_hwmon_data *)dev_get_drvdata(dev); + + if (nr > SENSOR_MAX_NUMBER || index >= data->sensors_number[nr]) { + pr_debug("Can not check the device"); + return -1; + } + + /* For the V3 version, index need offset */ + if (data->bfpi_version == 0x03 && nr != SENSOR_VOLTAGE_NUMBER) + index++; + + dev_id = (index << 12) | (nr << 8); + err = asus_wmi_evaluate_method(HWM_METHOD_ID, dev_id, 0, &retval); + if (err) + return err; + + /* For the V3 version, need to convert the raw value*/ + if (nr == SENSOR_VOLTAGE_NUMBER && data->bfpi_version == 0x03) { + switch (index) { + case 0: /* VCORE */ + retval = retval * 16; + break; + case 1: /* +5V */ + retval = (retval * 2008) / 50; + break; + case 2: /* AVCC */ + retval = retval * 16; + break; + case 3: /* +3.3V */ + retval = retval * 16; + break; + case 4: /* +12V */ + retval = retval * 96; + break; + case 5: /* VCOREREFIN */ + retval = (retval * 552) / 41; + break; + case 6: /* VIN4 */ + retval = retval * 8; + break; + case 7: /* 3VSB */ + retval = retval * 16; + break; + case 8: /* VBAT */ + retval = retval * 16; + break; + default: + break; + } + } else if (nr == SENSOR_TEMP_NUMBER && data->bfpi_version == 0x03) + retval = retval * 1000; + + return sprintf(buf, "%d\n", retval); +} + +static int aaeon_hwmon_create_sub_sysfs_fs(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, + int sensor_number, + u32 sensor_mask, + int bfpi_version) +{ + int i, err = 0; + + for (i = 0; i < sensor_number; i++) { + if (bfpi_version == 0x03 || sensor_mask & BIT(i)) { + err = device_create_file(&pdev->dev, &attr[2 * i].dev_attr); + if (err) + break; + err = device_create_file(&pdev->dev, &attr[2 * i + 1].dev_attr); + if (err) + break; + } + } + + return err; +} + +static int +aaeon_hwmon_create_sysfs_files(struct platform_device *pdev, struct aaeon_hwmon_data *data) +{ + int err; + + /* register sysfs interface files */ + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + return err; + + /* registe sysfs to dump sensors BFPI version */ + err = device_create_file(&pdev->dev, &info_sys_nodes_atts[0].dev_attr); + if (err) + return err; + + /* create temperature name and value node */ + err = aaeon_hwmon_create_sub_sysfs_fs(pdev, temp_sys_nodes_atts, + data->sensors_number[SENSOR_TEMP_NUMBER], + data->temp_bitmap, data->bfpi_version); + if (err) + return err; + + /* create fan name and value node */ + err = aaeon_hwmon_create_sub_sysfs_fs(pdev, fan_sys_nodes_atts, + data->sensors_number[SENSOR_FAN_NUMBER], + data->fan_bitmap, data->bfpi_version); + if (err) + return err; + + /* create voltage name and value node */ + err = aaeon_hwmon_create_sub_sysfs_fs(pdev, voltage_sys_nodes_atts, + data->sensors_number[SENSOR_VOLTAGE_NUMBER], + data->voltage_bitmap, data->bfpi_version); + if (err) + return err; + + return 0; +} + +static void aaeon_hwmon_remove_sub_sysfs_fs(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, + int sensor_number, + u32 sensor_mask, + int bfpi_version) +{ + int i; + + for (i = 0; i < sensor_number; i++) { + if (bfpi_version == 0x03 || sensor_mask & BIT(i)) { + device_remove_file(&pdev->dev, &attr[2 * i].dev_attr); + device_remove_file(&pdev->dev, &attr[2 * i + 1].dev_attr); + } + } +} + +static void +aaeon_hwmon_remove_sysfs_files(struct platform_device *pdev, + struct aaeon_hwmon_data *data) +{ + /* degister sysfs interface files */ + device_remove_file(&pdev->dev, &dev_attr_name); + + /* degiste sysfs to dump sensors BFPI version */ + device_remove_file(&pdev->dev, &info_sys_nodes_atts[0].dev_attr); + + /* remove temperature name and value node */ + aaeon_hwmon_remove_sub_sysfs_fs(pdev, temp_sys_nodes_atts, + data->sensors_number[SENSOR_TEMP_NUMBER], + data->temp_bitmap, + data->bfpi_version); + + /* remove fan name and value node */ + aaeon_hwmon_remove_sub_sysfs_fs(pdev, fan_sys_nodes_atts, + data->sensors_number[SENSOR_FAN_NUMBER], + data->fan_bitmap, + data->bfpi_version); + + /* remove voltage name and value node */ + aaeon_hwmon_remove_sub_sysfs_fs(pdev, voltage_sys_nodes_atts, + data->sensors_number[SENSOR_VOLTAGE_NUMBER], + data->voltage_bitmap, + data->bfpi_version); +} + +static int aaeon_hwmon_remove(struct platform_device *pdev) +{ + struct aaeon_hwmon_data *data = platform_get_drvdata(pdev); + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + aaeon_hwmon_remove_sysfs_files(pdev, data); + + return 0; +} + +static int aaeon_get_version(void) +{ + int err, retval; + u32 dev_id = 0x00; + + err = asus_wmi_evaluate_method(AAEON_VERSION_METHOD_ID, dev_id, 0, + &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_hwmon_init_drv_data(struct aaeon_hwmon_data *data) +{ + int err; + + data->bfpi_version = aaeon_get_version(); + if (data->bfpi_version < 0) { + pr_debug("Error BFPI verion\n"); + return -1; + } + + if (data->bfpi_version == 0x03) { + /* set the number of bits in temp bitmap */ + data->sensors_number[SENSOR_TEMP_NUMBER] = + ARRAY_SIZE(temp_sensors_name_table_V3); + data->temp_names = temp_sensors_name_table_V3; + + /* set the number of bits in fan bitmap */ + data->sensors_number[SENSOR_FAN_NUMBER] = + ARRAY_SIZE(fan_sensors_name_table_V3); + data->fan_names = fan_sensors_name_table_V3; + + /* set the number of bits in voltage bitmap */ + data->sensors_number[SENSOR_VOLTAGE_NUMBER] = + ARRAY_SIZE(voltage_sensors_name_table_V3); + data->voltage_names = voltage_sensors_name_table_V3; + } else { + /* set the number of bits in temp bitmap */ + data->sensors_number[SENSOR_TEMP_NUMBER] = + ARRAY_SIZE(temp_sensors_name_table); + data->temp_names = temp_sensors_name_table; + + /* set the number of bits in fan bitmap */ + data->sensors_number[SENSOR_FAN_NUMBER] = + ARRAY_SIZE(fan_sensors_name_table); + data->fan_names = fan_sensors_name_table; + + /* set the number of bits in voltage bitmap */ + data->sensors_number[SENSOR_VOLTAGE_NUMBER] = + ARRAY_SIZE(voltage_sensors_name_table); + data->voltage_names = voltage_sensors_name_table; + } + + /* get temp supported bitmap */ + err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID, + BITMAP_TEMP_ARG, 0, &data->temp_bitmap); + if (err) + return err; + + /* get fan supported bitmap */ + err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID, + BITMAP_FAN_ARG, 0, &data->fan_bitmap); + if (err) + return err; + + /* get voltage supported bitmap */ + err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID, + BITMAP_VOLTAGE_ARG, 0, &data->voltage_bitmap); + if (err) + return err; + + return 0; +} + +static int aaeon_hwmon_probe(struct platform_device *pdev) +{ + int err; + struct aaeon_hwmon_data *data; + + pr_debug("aaeon hwomon device probe (support V3)!\n"); + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + pr_info("AAEON Management GUID not found\n"); + return -ENODEV; + } + + data = devm_kzalloc(&pdev->dev, sizeof(struct aaeon_hwmon_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + err = aaeon_hwmon_init_drv_data(data); + if (err) { + pr_info("Error to get sensor support bitmap\n"); + goto exit; + } + + if (data->bfpi_version != 0x03 && data->temp_bitmap == 0 && + data->fan_bitmap == 0 && data->voltage_bitmap == 0) { + pr_debug("No sensors found\n"); + err = -ENODEV; + goto exit; + } + + platform_set_drvdata(pdev, data); + err = aaeon_hwmon_create_sysfs_files(pdev, data); + if (err) + goto exit; + + data->hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "AAEON_HWM", + data, + NULL, + NULL); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; + goto exit_unregister_sysfs; + } + + return 0; + +exit_unregister_sysfs: + aaeon_hwmon_remove(pdev); +exit: + return err; +} + +static struct platform_driver aaeon_hwmon_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = aaeon_hwmon_probe, + .remove = aaeon_hwmon_remove, +}; + +module_platform_driver_probe(aaeon_hwmon_driver, aaeon_hwmon_probe); + +MODULE_ALIAS("platform:hwmon-aaeon"); +MODULE_DESCRIPTION("AAEON Hardware Monitoring Driver"); +MODULE_AUTHOR("Edward Lin "); +MODULE_AUTHOR("Kunyang Fan "); +MODULE_LICENSE("GPL v2"); From a316709046b043c7ff25efa99963d3219bc686a7 Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:57:02 +0800 Subject: [PATCH 356/521] UBUNTU: ODM: leds: add driver for AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This patch adds support for the led devices which can be controlled from sysfs through ASUS WMI interface. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/dea44875e926deababd0efc7d3200319562e3337 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/leds/Kconfig | 12 ++++ drivers/leds/Makefile | 1 + drivers/leds/leds-aaeon.c | 142 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 drivers/leds/leds-aaeon.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index f4a0a3c8c8705e..22748404a7f061 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -72,6 +72,18 @@ config LEDS_88PM860X This option enables support for on-chip LED drivers found on Marvell Semiconductor 88PM8606 PMIC. +config LEDS_AAEON + tristate "AAEON LED driver" + depends on X86 + depends on UBUNTU_ODM_DRIVERS + select MFD_AAEON + help + This led driver adds support for LED brightness control on Single + Board Computers produced by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + config LEDS_AN30259A tristate "LED support for Panasonic AN30259A" depends on LEDS_CLASS && I2C && OF diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8fdb45d5b43930..2b9506f9a69487 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_KUNIT_TEST) += led-test.o # LED Platform Drivers (keep this sorted, M-| sort) obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o +obj-$(CONFIG_LEDS_AAEON) += leds-aaeon.o obj-$(CONFIG_LEDS_ACER_A500) += leds-acer-a500.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o diff --git a/drivers/leds/leds-aaeon.c b/drivers/leds/leds-aaeon.c new file mode 100644 index 00000000000000..10090a4bff6545 --- /dev/null +++ b/drivers/leds/leds-aaeon.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AAEON LED driver + * + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Kunyang Fan + * + * 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. + */ +#include +#include +#include +#include +#include +#include + +#define DRVNAME "led_aaeon" +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +#define GET_LED_NUMBER_ID 0x00060000 +#define GET_LED_METHOD_ID 0x00060001 +#define SET_LED_METHOD_ID 0x00060002 +#define GET_LED_NUMBER_METHOD_ID 0x10 + + +struct aaeon_led_data { + int id; + struct led_classdev cdev; +}; + +static int aaeon_led_get_number(void) +{ + int err, retval; + + err = asus_wmi_evaluate_method(GET_LED_NUMBER_ID, + GET_LED_NUMBER_METHOD_ID, + 0, &retval); + if (err) + return err; + + return retval; +} + +static enum led_brightness aaeon_led_brightness_get(struct led_classdev + *cdev) +{ + int err, brightness; + struct aaeon_led_data *led = + container_of(cdev, struct aaeon_led_data, cdev); + u32 arg0; + + arg0 = (u32)(led->id & 0xF); + err = asus_wmi_evaluate_method(GET_LED_METHOD_ID, arg0, 0, &brightness); + if (err) + return err; + + return brightness; +}; + +static void aaeon_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + int err, retval; + struct aaeon_led_data *led = + container_of(cdev, struct aaeon_led_data, cdev); + u32 arg0; + + arg0 = (u32)(led->id & 0xF); + if (brightness != LED_OFF) + arg0 |= BIT(16); + + err = asus_wmi_evaluate_method(SET_LED_METHOD_ID, arg0, 0, &retval); +}; + +static int __init aaeon_add_led_device(struct platform_device *pdev, + int id) +{ + struct aaeon_led_data *led; + + led = devm_kzalloc(&pdev->dev, sizeof(struct aaeon_led_data), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->id = id; + led->cdev.brightness_get = aaeon_led_brightness_get; + led->cdev.brightness_set = aaeon_led_brightness_set; + led->cdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "led:%d:", id); + + if (!led->cdev.name) + return -ENOMEM; + + return devm_led_classdev_register(&pdev->dev, &led->cdev); +} + +static int aaeon_led_probe(struct platform_device *pdev) +{ + int err = -ENODEV, i; + int led_number = 0; + + pr_debug("aaeon led device probe!\n"); + /* Prevent other drivers adding this platfom device */ + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + pr_debug("AAEON Management GUID not found\n"); + return -ENODEV; + } + + /* Query the number of led devices board support */ + led_number = aaeon_led_get_number(); + + /* + * If the number is 0 or can't get the number of leds, + * no need to register any led device node. + */ + if (led_number <= 0) + return -ENODEV; + + for (i = 0; i < led_number; i++) { + err = aaeon_add_led_device(pdev, i); + if (err) + break; + } + + return err; +} + +static struct platform_driver aaeon_led_driver = { + .driver = { + .name = "leds-aaeon", + }, +}; + +module_platform_driver_probe(aaeon_led_driver, aaeon_led_probe); + +MODULE_ALIAS("platform:leds-aaeon"); +MODULE_DESCRIPTION("AAEON LED Driver"); +MODULE_AUTHOR("Kunyang Fan "); +MODULE_LICENSE("GPL v2"); From c8734d81412030b68bb78ac55e2c0ffa5ffde95d Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:56:59 +0800 Subject: [PATCH 357/521] UBUNTU: ODM: gpio: add driver for AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This patch add support for the GPIO pins whose control are transported to BIOS through ASUS WMI interface. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/37faff18ec5a41729832e8229cda02d7c0e47f72 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpio/Kconfig | 12 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-aaeon.c | 205 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 drivers/gpio/gpio-aaeon.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 020e51e30317a8..254bd37f75acf8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1786,6 +1786,18 @@ endmenu menu "PCI GPIO expanders" depends on PCI +config GPIO_AAEON + tristate "AAEON GPIO support" + depends on ASUS_WMI + depends on UBUNTU_ODM_DRIVERS + select MFD_AAEON + help + Say yes here to support GPIO pins on Single Board Computers produced + by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + config GPIO_AMD8111 tristate "AMD 8111 GPIO driver" depends on X86 || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b267598b517de0..d2ee444bebe31b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o +obj-$(CONFIG_GPIO_AAEON) += gpio-aaeon.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o diff --git a/drivers/gpio/gpio-aaeon.c b/drivers/gpio/gpio-aaeon.c new file mode 100644 index 00000000000000..3183c45dd19461 --- /dev/null +++ b/drivers/gpio/gpio-aaeon.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AAEON GPIO driver + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Edward Lin + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "gpio_aaeon" +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +#define GET_GPIO_NUMBER_ID 0x00010000 +#define GET_LEVEL_METHOD_ID 0x00010001 +#define SET_LEVEL_METHOD_ID 0x00010002 +#define GET_DIRECTION_METHOD_ID 0x00010003 +#define SET_DIRECTION_METHOD_ID 0x00010004 +#define GET_SIO_NUMBER_METHOD_ID 0xF0010 + +struct aaeon_gpio_bank { + struct gpio_chip chip; + unsigned int regbase; + struct aaeon_gpio_data *data; +}; + +struct aaeon_gpio_data { + int nr_bank; + struct aaeon_gpio_bank *bank; +}; + +static int aaeon_gpio_get_number(void); +static int aaeon_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset); +static int aaeon_gpio_output_set_direction(struct gpio_chip *chip, + unsigned int offset, int value); +static int aaeon_gpio_input_set_direction(struct gpio_chip *chip, + unsigned int offset); +static int aaeon_gpio_get(struct gpio_chip *chip, + unsigned int offset); +static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value); + +#define AAEON_GPIO_BANK(_base, _ngpio, _regbase) \ +{ \ + .chip = { \ + .label = DRVNAME, \ + .owner = THIS_MODULE, \ + .get_direction = aaeon_gpio_get_direction, \ + .direction_input = aaeon_gpio_input_set_direction, \ + .direction_output = aaeon_gpio_output_set_direction, \ + .get = aaeon_gpio_get, \ + .set = aaeon_gpio_set, \ + .base = _base, \ + .ngpio = _ngpio, \ + .can_sleep = true, \ + }, \ + .regbase = _regbase, \ +} + +static struct aaeon_gpio_bank aaeon_gpio_bank[] = { + AAEON_GPIO_BANK(0, 0, 0xF0), +}; + +static int aaeon_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + int err, retval; + u32 dev_id = 0x0; + + dev_id |= offset; + err = asus_wmi_evaluate_method(GET_DIRECTION_METHOD_ID, dev_id, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_gpio_input_set_direction(struct gpio_chip *chip, + unsigned int offset) +{ + int err, retval; + u32 dev_id; + + dev_id = BIT(16) | offset; + err = asus_wmi_evaluate_method(SET_DIRECTION_METHOD_ID, dev_id, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_gpio_output_set_direction(struct gpio_chip *chip, + unsigned int offset, int value) +{ + int err, retval; + u32 dev_id = 0x0; + + dev_id |= offset; + err = asus_wmi_evaluate_method(SET_DIRECTION_METHOD_ID, dev_id, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + int err, retval; + u32 dev_id = 0x0; + + dev_id |= offset; + err = asus_wmi_evaluate_method(GET_LEVEL_METHOD_ID, dev_id, 0, &retval); + if (err) + return err; + + return retval; +} + +static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + int retval; + u32 dev_id = offset; + + if (value) + dev_id = BIT(16) | dev_id; + + asus_wmi_evaluate_method(SET_LEVEL_METHOD_ID, dev_id, 0, &retval); +} + +static int aaeon_gpio_get_number(void) +{ + int err, retval; + + err = asus_wmi_evaluate_method(GET_GPIO_NUMBER_ID, + GET_SIO_NUMBER_METHOD_ID, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int __init aaeon_gpio_probe(struct platform_device *pdev) +{ + int err, i; + int dio_number = 0; + struct aaeon_gpio_data *data; + struct aaeon_gpio_bank *bank; + + /* Prevent other drivers adding this platfom device */ + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + pr_debug("AAEON Management GUID not found\n"); + return -ENODEV; + } + + dio_number = aaeon_gpio_get_number(); + if (dio_number < 0) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->nr_bank = ARRAY_SIZE(aaeon_gpio_bank); + data->bank = aaeon_gpio_bank; + platform_set_drvdata(pdev, data); + bank = &data->bank[0]; + bank->chip.parent = &pdev->dev; + bank->chip.ngpio = dio_number; + bank->data = data; + err = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank); + if (err) + pr_debug("Failed to register gpiochip %d: %d\n", i, err); + + return err; +} + +static struct platform_driver aaeon_gpio_driver = { + .driver = { + .name = "gpio-aaeon", + }, +}; + +module_platform_driver_probe(aaeon_gpio_driver, aaeon_gpio_probe); + +MODULE_ALIAS("platform:gpio-aaeon"); +MODULE_DESCRIPTION("AAEON GPIO Driver"); +MODULE_AUTHOR("Edward Lin "); +MODULE_LICENSE("GPL v2"); From 311ff2ac38b06d70edc02aaa3a21316362d2b1d9 Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:56:58 +0800 Subject: [PATCH 358/521] UBUNTU: ODM: mfd: Add support for IO functions of AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This adds the supports for multiple IO functions of the AAEON x86 devices and makes use of the WMI interface to control the these IO devices including: - GPIO - LED - Watchdog - HWMON It also adds the mfd child device drivers to support the above IO functions. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/9c320f62f5890f8e78485453db7c0eec68884d34 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- MAINTAINERS | 12 +++++++ drivers/mfd/Kconfig | 12 +++++++ drivers/mfd/Makefile | 1 + drivers/mfd/mfd-aaeon.c | 77 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 drivers/mfd/mfd-aaeon.c diff --git a/MAINTAINERS b/MAINTAINERS index 74848d12afb7dc..3a1f16a36f055b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -200,6 +200,18 @@ M: Linus Walleij F: Documentation/devicetree/bindings/power/supply/*ab8500* F: drivers/power/supply/*ab8500* +AAEON DEVICE DRIVER WITH WMI INTERFACE +M: Edward Lin +M: Kunyang Fan +M: Frank Hsieh +M: Jacob Wu +S: Supported +F: drivers/gpio/gpio-aaeon.c +F: drivers/hwmon/hwmon-aaeon.c +F: drivers/leds/leds-aaeon.c +F: drivers/mfd/mfd-aaeon.c +F: drivers/watchdog/wdt_aaeon.c + ABI/API L: linux-api@vger.kernel.org F: include/linux/syscalls.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7192c9d1d268e9..1463c0b0a55d3b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2402,6 +2402,18 @@ config MFD_QCOM_PM8008 under it in the device tree. Additional drivers must be enabled in order to use the functionality of the device. +config MFD_AAEON + tristate "AAEON WMI MFD devices" + depends on ASUS_WMI + depends on UBUNTU_ODM_DRIVERS + help + Say yes here to support mltiple IO devices on Single Board Computers + produced by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28afa..438827883d8061 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -295,6 +295,7 @@ obj-$(CONFIG_MFD_LS2K_BMC_CORE) += ls2k-bmc-core.o obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o +obj-$(CONFIG_MFD_AAEON) += mfd-aaeon.o obj-$(CONFIG_MFD_QNAP_MCU) += qnap-mcu.o diff --git a/drivers/mfd/mfd-aaeon.c b/drivers/mfd/mfd-aaeon.c new file mode 100644 index 00000000000000..9d2efde53cad38 --- /dev/null +++ b/drivers/mfd/mfd-aaeon.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * UP Board main platform driver and FPGA configuration support + * + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Kunyang_Fan + * + * 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 +#include +#include + +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +struct aaeon_wmi_priv { + const struct mfd_cell *cells; + size_t ncells; +}; + +static const struct mfd_cell aaeon_mfd_cells[] = { + { .name = "gpio-aaeon" }, + { .name = "hwmon-aaeon"}, + { .name = "leds-aaeon"}, + { .name = "wdt-aaeon"}, +}; + +static const struct aaeon_wmi_priv aaeon_wmi_priv_data = { + .cells = aaeon_mfd_cells, + .ncells = ARRAY_SIZE(aaeon_mfd_cells), +}; + +static int aaeon_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct aaeon_wmi_priv *priv; + + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + dev_info(&wdev->dev, "AAEON Management GUID not found\n"); + return -ENODEV; + } + + + priv = (struct aaeon_wmi_priv *)context; + dev_set_drvdata(&wdev->dev, priv); + + return devm_mfd_add_devices(&wdev->dev, 0, priv->cells, + priv->ncells, NULL, 0, NULL); +} + +static const struct wmi_device_id aaeon_wmi_id_table[] = { + { AAEON_WMI_MGMT_GUID, (void *)&aaeon_wmi_priv_data }, + {} +}; + +static struct wmi_driver aaeon_wmi_driver = { + .driver = { + .name = "mfd-aaeon", + }, + .id_table = aaeon_wmi_id_table, + .probe = aaeon_wmi_probe, +}; + +module_wmi_driver(aaeon_wmi_driver); + +MODULE_DEVICE_TABLE(wmi, aaeon_wmi_id_table); +MODULE_AUTHOR("Kunyang Fan "); +MODULE_DESCRIPTION("AAEON Board WMI driver"); +MODULE_LICENSE("GPL v2"); From 4acda406955e10a4b09c57f97b14640b76784f66 Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Tue, 24 Aug 2021 15:26:59 +0800 Subject: [PATCH 359/521] UBUNTU: ODM: mfd: Check AAEON BFPI version before adding device BugLink: https://bugs.launchpad.net/bugs/1937897 For the below: error log occurring in some devices: gpio gpiochip0: (gpio_aaeon): tried to insert a GPIO chip with zero lines gpiochip_add_data_with_key: GPIOs 0..-1 (gpio_aaeon) failed to register Add the BFPI version checking mechanism to prevent error log bumping. Fixes: 424945128781 ("UBUNTU: ODM: mfd: Add support for IO functions of AAEON devices") Signed-off-by: Kunyang_Fan Acked-by: Kai-Heng Feng Signed-off-by: Chia-Lin Kao (AceLan) Signed-off-by: Paolo Pisati Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/3d4fbe8d0b4b0f023fbfc2b45e6005b470a5db37 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/mfd/mfd-aaeon.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mfd/mfd-aaeon.c b/drivers/mfd/mfd-aaeon.c index 9d2efde53cad38..74211d01ce6566 100644 --- a/drivers/mfd/mfd-aaeon.c +++ b/drivers/mfd/mfd-aaeon.c @@ -16,12 +16,17 @@ #include #include #include +#include #include #include #include #define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" +#define WMI_REPORT_CAPABILITY_METHOD 0x00000000 +#define MAX_BFPI_VERSION 255 +#define GET_REVISION_ID 0x00 + struct aaeon_wmi_priv { const struct mfd_cell *cells; size_t ncells; @@ -39,6 +44,21 @@ static const struct aaeon_wmi_priv aaeon_wmi_priv_data = { .ncells = ARRAY_SIZE(aaeon_mfd_cells), }; +static int aaeon_wmi_check_device(void) +{ + int err; + int retval; + + err = asus_wmi_evaluate_method(WMI_REPORT_CAPABILITY_METHOD, GET_REVISION_ID, 0, + &retval); + if (err) + return -ENODEV; + if (retval < 3 || retval > MAX_BFPI_VERSION) + return -ENODEV; + + return 0; +} + static int aaeon_wmi_probe(struct wmi_device *wdev, const void *context) { struct aaeon_wmi_priv *priv; @@ -48,6 +68,8 @@ static int aaeon_wmi_probe(struct wmi_device *wdev, const void *context) return -ENODEV; } + if (aaeon_wmi_check_device()) + return -ENODEV; priv = (struct aaeon_wmi_priv *)context; dev_set_drvdata(&wdev->dev, priv); From 025d932a8501de3d569dfdcd5b584adde082a549 Mon Sep 17 00:00:00 2001 From: Timo Aaltonen Date: Mon, 5 Aug 2024 14:48:08 +0300 Subject: [PATCH 360/521] UBUNTU: SAUCE: hwmon: Fix aaeon driver for 6.11. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the build after commit 0edb555a65d1ef047a9805051c36922b52a38a9d Author: Uwe Kleine-König Date: Mon Oct 9 12:37:26 2023 +0200 platform: Make platform_driver::remove() return void Signed-off-by: Timo Aaltonen Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/6dd48b760b813aae56a9b0d55d91b2e52e3ac73e Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/hwmon/hwmon-aaeon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/hwmon-aaeon.c b/drivers/hwmon/hwmon-aaeon.c index 146da10309bbb9..80bf4d1224f3fa 100644 --- a/drivers/hwmon/hwmon-aaeon.c +++ b/drivers/hwmon/hwmon-aaeon.c @@ -49,7 +49,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *devattr, char *buf); static int aaeon_get_version(void); static int aaeon_hwmon_probe(struct platform_device *pdev); -static int aaeon_hwmon_remove(struct platform_device *pdev); +static void aaeon_hwmon_remove(struct platform_device *pdev); static const char * const temp_sensors_name_table[] = { "CPU_Temp", @@ -409,7 +409,7 @@ aaeon_hwmon_remove_sysfs_files(struct platform_device *pdev, data->bfpi_version); } -static int aaeon_hwmon_remove(struct platform_device *pdev) +static void aaeon_hwmon_remove(struct platform_device *pdev) { struct aaeon_hwmon_data *data = platform_get_drvdata(pdev); @@ -418,7 +418,7 @@ static int aaeon_hwmon_remove(struct platform_device *pdev) aaeon_hwmon_remove_sysfs_files(pdev, data); - return 0; + return; } static int aaeon_get_version(void) From 628a729cfb86b4cbe6bb143fec4b630b52b8e3dd Mon Sep 17 00:00:00 2001 From: Timo Aaltonen Date: Wed, 20 Aug 2025 13:47:36 +0300 Subject: [PATCH 361/521] UBUNTU: SAUCE: gpio: aaeon: use new GPIO line value setter callbacks Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/gpio/gpio-aaeon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aaeon.c b/drivers/gpio/gpio-aaeon.c index 3183c45dd19461..fb3adf8f226af8 100644 --- a/drivers/gpio/gpio-aaeon.c +++ b/drivers/gpio/gpio-aaeon.c @@ -50,7 +50,7 @@ static int aaeon_gpio_input_set_direction(struct gpio_chip *chip, unsigned int offset); static int aaeon_gpio_get(struct gpio_chip *chip, unsigned int offset); -static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, int value); #define AAEON_GPIO_BANK(_base, _ngpio, _regbase) \ @@ -131,7 +131,7 @@ static int aaeon_gpio_get(struct gpio_chip *chip, unsigned int offset) return retval; } -static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { int retval; @@ -140,7 +140,7 @@ static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, if (value) dev_id = BIT(16) | dev_id; - asus_wmi_evaluate_method(SET_LEVEL_METHOD_ID, dev_id, 0, &retval); + return asus_wmi_evaluate_method(SET_LEVEL_METHOD_ID, dev_id, 0, &retval); } static int aaeon_gpio_get_number(void) From e9e494ca50dc67fdc7aa120119613778c70ca703 Mon Sep 17 00:00:00 2001 From: Timo Aaltonen Date: Wed, 7 Jan 2026 12:54:41 +0200 Subject: [PATCH 362/521] UBUNTU: SAUCE: aaeon: The modules need to import ASUS_WMI now Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/gpio/gpio-aaeon.c | 1 + drivers/hwmon/hwmon-aaeon.c | 1 + drivers/leds/leds-aaeon.c | 1 + drivers/mfd/mfd-aaeon.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/gpio/gpio-aaeon.c b/drivers/gpio/gpio-aaeon.c index fb3adf8f226af8..e57f41eed3ec0a 100644 --- a/drivers/gpio/gpio-aaeon.c +++ b/drivers/gpio/gpio-aaeon.c @@ -203,3 +203,4 @@ MODULE_ALIAS("platform:gpio-aaeon"); MODULE_DESCRIPTION("AAEON GPIO Driver"); MODULE_AUTHOR("Edward Lin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); diff --git a/drivers/hwmon/hwmon-aaeon.c b/drivers/hwmon/hwmon-aaeon.c index 80bf4d1224f3fa..ea6df3c61a05f1 100644 --- a/drivers/hwmon/hwmon-aaeon.c +++ b/drivers/hwmon/hwmon-aaeon.c @@ -566,3 +566,4 @@ MODULE_DESCRIPTION("AAEON Hardware Monitoring Driver"); MODULE_AUTHOR("Edward Lin "); MODULE_AUTHOR("Kunyang Fan "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); diff --git a/drivers/leds/leds-aaeon.c b/drivers/leds/leds-aaeon.c index 10090a4bff6545..4034dfaba9eab0 100644 --- a/drivers/leds/leds-aaeon.c +++ b/drivers/leds/leds-aaeon.c @@ -140,3 +140,4 @@ MODULE_ALIAS("platform:leds-aaeon"); MODULE_DESCRIPTION("AAEON LED Driver"); MODULE_AUTHOR("Kunyang Fan "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); diff --git a/drivers/mfd/mfd-aaeon.c b/drivers/mfd/mfd-aaeon.c index 74211d01ce6566..de702c45a3b6dc 100644 --- a/drivers/mfd/mfd-aaeon.c +++ b/drivers/mfd/mfd-aaeon.c @@ -97,3 +97,4 @@ MODULE_DEVICE_TABLE(wmi, aaeon_wmi_id_table); MODULE_AUTHOR("Kunyang Fan "); MODULE_DESCRIPTION("AAEON Board WMI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); From df9bb8f8e0f33b61a03f3e5621efe09fe9a24505 Mon Sep 17 00:00:00 2001 From: zouxiaoh Date: Wed, 23 Feb 2022 10:21:25 +0800 Subject: [PATCH 363/521] BACKPORT: UBUNTU: SAUCE: iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs BugLink: https://bugs.launchpad.net/bugs/1958004 Intel IPU(Image Processing Unit) has its own (IO)MMU hardware, The IPU driver allocates its own page table that is not mapped via the DMA, and thus the Intel IOMMU driver blocks access giving this error: DMAR: DRHD: handling fault status reg 3 DMAR: [DMA Read] Request device [00:05.0] PASID ffffffff fault addr 76406000 [fault reason 06] PTE Read access is not set As IPU is not an external facing device which is not risky, so use IOMMU passthrough mode for Intel IPUs. Change-Id: I6dcccdadac308cf42e20a18e1b593381391e3e6b Depends-On: Iacd67578e8c6a9b9ac73285f52b4081b72fb68a6 Tracked-On: #JIITL8-411 Signed-off-by: Bingbu Cao Signed-off-by: zouxiaoh Signed-off-by: Xu Chongyang (cherry picked from https://github.com/intel/ipu6-drivers/blob/5d5526d2b2811aa52590c2fa513ba989e7e594ab/patch/IOMMU-passthrough-for-intel-ipu.diff) Signed-off-by: You-Sheng Yang Signed-off-by: Paolo Pisati Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai [ Mingcong Bai: Fixed a minor post-7.0.9 merge conflict in drivers/iommu/intel/iommu.c ] Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 4d0e65bc131d77..e7f53dec5a67fe 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -37,6 +37,12 @@ #define IS_GFX_DEVICE(pdev) pci_is_display(pdev) #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB) #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) +#define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \ + ((pdev)->device == 0x9a19 || \ + (pdev)->device == 0x9a39 || \ + (pdev)->device == 0x4e19 || \ + (pdev)->device == 0x465d || \ + (pdev)->device == 0x1919)) #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) #define IOAPIC_RANGE_START (0xfee00000) @@ -202,12 +208,14 @@ int intel_iommu_sm = IS_ENABLED(CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON); int intel_iommu_enabled = 0; EXPORT_SYMBOL_GPL(intel_iommu_enabled); +static int dmar_map_ipu = 1; static int intel_iommu_superpage = 1; static int iommu_identity_mapping; static int iommu_skip_te_disable; static int disable_igfx_iommu; #define IDENTMAP_AZALIA 4 +#define IDENTMAP_IPU 8 const struct iommu_ops intel_iommu_ops; @@ -1398,6 +1406,9 @@ static int device_def_domain_type(struct device *dev) if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev)) return IOMMU_DOMAIN_IDENTITY; + + if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev)) + return IOMMU_DOMAIN_IDENTITY; } return 0; @@ -1688,6 +1699,9 @@ static int __init init_dmars(void) iommu_set_root_entry(iommu); } + if (!dmar_map_ipu) + iommu_identity_mapping |= IDENTMAP_IPU; + check_tylersburg_isoch(); /* @@ -3979,6 +3993,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); +static void quirk_iommu_ipu(struct pci_dev *dev) +{ + if (!IS_INTEL_IPU(dev)) + return; + + if (risky_device(dev)) + return; + + pci_info(dev, "Passthrough IOMMU for integrated Intel IPU\n"); + dmar_map_ipu = 0; +} + +/* disable IPU dmar support */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_iommu_ipu); + static void quirk_iommu_rwbf(struct pci_dev *dev) { if (risky_device(dev)) From 2dd9ebc04ce8ec04365526d181b3740aaefd8863 Mon Sep 17 00:00:00 2001 From: zouxiaoh Date: Wed, 23 Feb 2022 10:21:25 +0800 Subject: [PATCH 364/521] UBUNTU: SAUCE: iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs on Raptor Lake BugLink: https://bugs.launchpad.net/bugs/1989041 Intel IPU(Image Processing Unit) has its own (IO)MMU hardware, The IPU driver allocates its own page table that is not mapped via the DMA, and thus the Intel IOMMU driver blocks access giving this error: DMAR: DRHD: handling fault status reg 3 DMAR: [DMA Read] Request device [00:05.0] PASID ffffffff fault addr 76406000 [fault reason 06] PTE Read access is not set As IPU is not an external facing device which is not risky, so use IOMMU passthrough mode for Intel IPUs. (backported from https://github.com/intel/ipu6-drivers/blob/89c6b99e4bec00a04b53f37cd8c3c01ce824a4ab/patch/IOMMU-passthrough-for-intel-ipu.diff) Signed-off-by: You-Sheng Yang Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index e7f53dec5a67fe..932ecc71de49f2 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -38,7 +38,8 @@ #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB) #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) #define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \ - ((pdev)->device == 0x9a19 || \ + ((pdev)->device == 0xa75d || \ + (pdev)->device == 0x9a19 || \ (pdev)->device == 0x9a39 || \ (pdev)->device == 0x4e19 || \ (pdev)->device == 0x465d || \ From 094f8b40a4216f217615ae60ac51c266918ce954 Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Thu, 26 Oct 2023 00:06:41 +0800 Subject: [PATCH 365/521] UBUNTU: SAUCE: iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs on Meteor Lake BugLink: https://bugs.launchpad.net/bugs/2031412 Signed-off-by: You-Sheng Yang Acked-by: Stefan Bader Acked-by: Roxana Nicolescu Signed-off-by: Roxana Nicolescu Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 932ecc71de49f2..426c6feac44ffa 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -41,6 +41,7 @@ ((pdev)->device == 0xa75d || \ (pdev)->device == 0x9a19 || \ (pdev)->device == 0x9a39 || \ + (pdev)->device == 0x7d19 || \ (pdev)->device == 0x4e19 || \ (pdev)->device == 0x465d || \ (pdev)->device == 0x1919)) From 62f7462609ac52a8e71304e7532fc69e11d96a53 Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 12 Aug 2024 14:28:31 +0800 Subject: [PATCH 366/521] BACKPORT: UBUNTU: SAUCE: iommu/intel: disable DMAR for SKL integrated gfx BugLink: https://bugs.launchpad.net/bugs/2062951 This patch addresses a screen flickering issues on systems with Skylake integrated graphics, identified under Ubuntu kernel version 6.8.0-x. Initially thought to be a regression from kernel 6.6, it has been determined that the flickering was caused by changes to the Ubuntu kernel configuration, specifically the enabling of CONFIG_INTEL_IOMMU_DEFAULT_ON and CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON. 77e530c1a864c ("UBUNTU: [Config] enable Intel DMA remapping by default") The problem persists in the latest drm tip, and there is an upstream bug addressing the issue. https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/11504 Before there is a real fix for the issue, we may have to add the affected GPU IDs to the quirk to disable its DMAR. Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Kuan-Ying Lee Acked-by: Aaron Jauregui Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0.9 merge conflict in drivers/iommu/intel/iommu.c ] Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 426c6feac44ffa..659ec5ac9a0690 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -3995,6 +3995,33 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); +/* SKL */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1906, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1913, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x190E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1915, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1902, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x190A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x190B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1917, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1916, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1921, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1912, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1923, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1926, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1927, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x192A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x192B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x192D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1932, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193D, quirk_iommu_igfx); + static void quirk_iommu_ipu(struct pci_dev *dev) { if (!IS_INTEL_IPU(dev)) From a3850fa6aaf21caf0d560dab7331ba71d804510b Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 18 Nov 2024 09:28:40 +0800 Subject: [PATCH 367/521] BACKPORT: UBUNTU: SAUCE: iommu/intel: disable DMAR for KBL and CML integrated gfx BugLink: https://bugs.launchpad.net/bugs/2086587 This patch addresses a screen flickering issues on systems with KBL and CML integrated graphics, identified under Ubuntu kernel version 6.8.0-x. Initially thought to be a regression from kernel 6.6, it has been determined that the flickering was caused by changes to the Ubuntu kernel configuration, specifically the enabling of CONFIG_INTEL_IOMMU_DEFAULT_ON and CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON. 77e530c1a864c ("UBUNTU: [Config] enable Intel DMA remapping by default") The problem persists in the latest drm tip, and there is an upstream bug addressing the issue. https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/11504 Before there is a real fix for the issue, we may have to add the affected GPU IDs to the quirk to disable its DMAR. Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Magali Lemes Signed-off-by: Paolo Pisati Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0.9 merge conflict in drivers/iommu/intel/iommu.c ] Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 659ec5ac9a0690..d473c445eecf20 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -4022,6 +4022,47 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193A, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193B, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193D, quirk_iommu_igfx); +/* KBL */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5902, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5906, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5908, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x590A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x590B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x590E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5912, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5913, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5915, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5916, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5917, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5921, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5923, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5926, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5927, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x593B, quirk_iommu_igfx); + +/* CML */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9B21, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA2, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA4, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA5, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA8, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BAA, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BAC, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC2, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC4, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC5, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC6, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC8, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BE6, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BF6, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9B41, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCA, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCC, quirk_iommu_igfx); + static void quirk_iommu_ipu(struct pci_dev *dev) { if (!IS_INTEL_IPU(dev)) From 522d9100c41d79bdb3cfbcceba27a17174883493 Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Wed, 31 Aug 2011 10:25:24 -0700 Subject: [PATCH 368/521] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Latitude E6520 use reboot=pci The Dell Latitude E6520 doesn't reboot unless reboot=pci is set. BugLink: http://bugs.launchpad.net/bugs/833705 Cc: Signed-off-by: Leann Ogasawara Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 0fed6d0d7e322c..de9f35ade26e57 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -490,7 +490,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"), }, }, - + { /* Handle problems with rebooting on the Latitude E6520. */ + .callback = set_pci_reboot, + .ident = "Dell Latitude E6520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6520"), + }, + }, { } }; From 8c9fbcfbb867abf5ed9679803cbde87177ba4aa3 Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Fri, 9 Sep 2011 13:23:51 -0700 Subject: [PATCH 369/521] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Optiplex 790 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/818933 The Dell Optiplex 790 doesn't reboot unless reboot=pci is set. Signed-off-by: Leann Ogasawara Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index de9f35ade26e57..0a72663093d3db 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -498,6 +498,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6520"), }, }, + { /* Handle problems with rebooting on the OptiPlex 790. */ + .callback = set_pci_reboot, + .ident = "Dell OptiPlex 790", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 790"), + }, + }, { } }; From c4ce927f4207645a9b86e9e77ef167c661c770ed Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Wed, 14 Sep 2011 11:27:20 -0700 Subject: [PATCH 370/521] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Optiplex 990 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/768039 The Dell Optiplex 990 doesn't reboot unless reboot=pci is set. Signed-off-by: Leann Ogasawara Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 0a72663093d3db..d7f5a9924f9280 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -506,6 +506,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 790"), }, }, + { /* Handle problems with rebooting on the OptiPlex 990. */ + .callback = set_pci_reboot, + .ident = "Dell OptiPlex 990", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"), + }, + }, { } }; From 671cf9de5c39133e03a3c290ca6bdafa4df25e08 Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Fri, 26 Aug 2011 07:30:16 -0700 Subject: [PATCH 371/521] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Latitude E6220 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/838402 The Dell Latitude E6220 doesn't reboot unless reboot=pci is set. Signed-off-by: Leann Ogasawara Acked-by: Tim Gardner Acked-by: Seth Forshee Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index d7f5a9924f9280..bfbac81d964705 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -514,6 +514,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"), }, }, + { /* Handle problems with rebooting on the Latitude E6220. */ + .callback = set_pci_reboot, + .ident = "Dell Latitude E6220", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6220"), + }, + }, { } }; From 426085796fbe3d1f1ab0003bee79fa5ab52ee0d3 Mon Sep 17 00:00:00 2001 From: "joseph.salisbury@canonical.com" Date: Tue, 16 Apr 2013 17:03:51 -0400 Subject: [PATCH 372/521] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Optiplex 390 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/800660 From: Leann Ogasawara Signed-off-by: Leann Ogasawara Signed-off-by: Joseph Salisbury Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index bfbac81d964705..ed427d8bd8f186 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -522,6 +522,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6220"), }, }, + { /* Handle problems with rebooting on the OptiPlex 390. */ + .callback = set_pci_reboot, + .ident = "Dell OptiPlex 390", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 390"), + }, + }, { } }; From 08c00ea5fa0d7139502bde2239ff18858d3d0b12 Mon Sep 17 00:00:00 2001 From: Xiangliang Yu Date: Thu, 7 Mar 2013 14:29:16 +0000 Subject: [PATCH 373/521] UBUNTU: SAUCE: (no-up) PCI: fix system hang issue of Marvell SATA host controller BugLink: http://bugs.launchpad.net/bugs/1159863 Hassle someone if this patch hasn't been removed by 13.10. See https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1159863/comments/2 Fix system hang issue: if first accessed resource file of BAR0 ~ BAR4, system will hang after executing lspci command Signed-off-by: Kamal Mostafa Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fcee338bf0a169..f1692c782f2dd8 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -302,6 +302,21 @@ static void quirk_mmio_always_on(struct pci_dev *dev) DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); +/* The BAR0 ~ BAR4 of Marvell 9125 device can't be accessed +* by IO resource file, and need to skip the files +*/ +static void quirk_marvell_mask_bar(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < 5; i++) + if (dev->resource[i].start) + dev->resource[i].start = + dev->resource[i].end = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9125, + quirk_marvell_mask_bar); + /* * The Mellanox Tavor device gives false positive parity errors. Disable * parity error reporting. From f44de181217d769bcecb899bd8d8b1b7a58e6446 Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Mon, 16 Mar 2020 17:27:21 +0800 Subject: [PATCH 374/521] BACKPORT: UBUNTU: SAUCE: Input: i8042 - fix the selftest retry logic BugLink: https://bugs.launchpad.net/bugs/1866734 It returns -NODEV at the first selftest timeout, so the retry logic doesn't work. Move the return outside of the while loop to make it real retry 5 times before returns -ENODEV. BTW, the origin loop will retry 6 times, also fix this. Signed-off-by: You-Sheng Yang (backported from https://lore.kernel.org/linux-input/20200310033640.14440-1-vicamo@gmail.com/) Signed-off-by: Paolo Pisati [ Mingcong Bai: Resolved a minor mrege conflict in drivers/input/serio/i8042.c ] Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 8bcce11cb7ce3d..4633fb1d32be3d 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -942,25 +942,28 @@ static int i8042_controller_selftest(void) { unsigned char param; int i = 0; + int ret; /* * We try this 5 times; on some really fragile systems this does not * take the first time... */ - do { - - if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { - pr_err("i8042 controller selftest timeout\n"); - return -ENODEV; - } + while (i++ < 5) { - if (param == I8042_RET_CTL_TEST) + ret = i8042_command(¶m, I8042_CMD_CTL_TEST); + if (ret) + pr_err("i8042 controller selftest timeout (%d/5)\n", i); + else if (param == I8042_RET_CTL_TEST) return 0; + else + dbg("i8042 controller selftest: %#x != %#x\n", + param, I8042_RET_CTL_TEST); - dbg("i8042 controller selftest: %#x != %#x\n", - param, I8042_RET_CTL_TEST); msleep(50); - } while (i++ < 5); + } + + if (ret) + return -ENODEV; #ifdef CONFIG_X86 /* From 7c4c29c61253c6fef39f0cb5385d2e2e3052a16e Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 15 Mar 2021 16:05:40 +0800 Subject: [PATCH 375/521] UBUNTU: SAUCE: Input: i8042 - add dmi quirk BugLink: https://bugs.launchpad.net/bugs/1919123 On some platforms, the EC doesn't support the register reading sequence for sentelic[1], and then make the EC can't respond commands for a while when probing. It leads to the keyboard non-responsive for around 10 seconds while waking up from s2idle. [ 44.304488] i8042: [9804] d4 -> i8042 (command) [ 44.304634] i8042: [9804] f3 -> i8042 (parameter) [ 44.304787] i8042: [9804] fa <- i8042 (interrupt, 1, 12) [ 44.304855] i8042: [9804] d4 -> i8042 (command) [ 44.304938] i8042: [9804] 66 -> i8042 (parameter) [ 44.337698] i8042: [9813] d4 -> i8042 (command) [ 44.905695] i8042: [9942] 88 -> i8042 (parameter) [ 45.497478] i8042: [10102] d4 -> i8042 (command) [ 46.098041] i8042: [10253] f3 -> i8042 (parameter) [ 46.098070] i8042: [10253] fe <- i8042 (interrupt, 1, 12) [ 46.718154] i8042: [10386] d4 -> i8042 (command) [ 47.309915] i8042: [10386] f4 -> i8042 (parameter) [ 47.918961] i8042: [10556] d4 -> i8042 (command) [ 48.402624] i8042: [10556] f6 -> i8042 (parameter) A DMI quirk to mark this platform doesn't have aux device could avoid those commands to be sent. And the system could still using i2c interface to communicate with the touchpad. 1. https://www.kernel.org/doc/html/v5.11/input/devices/sentelic.html#programming-sequence-for-register-reading-writing Signed-off-by: Chia-Lin Kao (AceLan) (cherry picked from https://lkml.org/lkml/2021/3/15/126) Signed-off-by: Chia-Lin Kao (AceLan) Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 4633fb1d32be3d..e065411853223f 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -183,6 +184,24 @@ static bool i8042_handle_data(int irq); static i8042_filter_t i8042_platform_filter; static void *i8042_platform_filter_context; +static int __init i8042_set_noaux(const struct dmi_system_id *dmi) +{ + i8042_noaux = true; + return 1; +} + +static const struct dmi_system_id i8042_quirks[] __initconst = { + { + .callback = i8042_set_noaux, + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5550"), + }, + }, + {}, +}; + void i8042_lock_chip(void) { mutex_lock(&i8042_mutex); @@ -1556,6 +1575,8 @@ static int i8042_probe(struct platform_device *dev) i8042_dritek_enable(); #endif + dmi_check_system(i8042_quirks); + if (!i8042_noaux) { error = i8042_setup_aux(); if (error && error != -ENODEV && error != -EBUSY) From f2aae97a66a5facdcc4ee20d3a72dff1a7c8837f Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Thu, 15 Dec 2022 14:03:38 +0100 Subject: [PATCH 376/521] UBUNTU: SAUCE: input: i8042: fix section mismatch warning Fix the following warning introduced by an old sauce patch: WARNING: modpost: vmlinux.o: section mismatch in reference: i8042_probe (section: .text) -> i8042_quirks (section: .init.rodata) Fixes: ec05d1e5ffd7 ("UBUNTU: SAUCE: Input: i8042 - add dmi quirk") Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index e065411853223f..c7638b3efc0e7f 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -190,7 +190,7 @@ static int __init i8042_set_noaux(const struct dmi_system_id *dmi) return 1; } -static const struct dmi_system_id i8042_quirks[] __initconst = { +static const struct dmi_system_id i8042_quirks[] = { { .callback = i8042_set_noaux, .ident = "Dell laptop", From 3b8bfe97c46e66dbda885051992f275406168d86 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Thu, 5 May 2022 14:20:14 +0800 Subject: [PATCH 377/521] UBUNTU: SAUCE: net: phy: marvell: Skip setting LED on Dell EMC board BugLink: https://bugs.launchpad.net/bugs/1971667 The board in question requires certain LED config, which is already configured by BIOS, to correctly show its networking status. However, Marvell PHY driver hardcodes LED value so we need a way to preserve the default set by BIOS. PHY maintainer asked for a "generic" approach which goes no where [1], so let's move on and use a quirk to handle it. [1] https://lore.kernel.org/lkml/20220420124053.853891-2-kai.heng.feng@canonical.com/ Signed-off-by: Kai-Heng Feng Acked-by: Tim Gardner Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/net/phy/marvell.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 7a578b5aa2ed60..8fde1f24e4ee1a 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -356,6 +357,16 @@ struct marvell_priv { u8 vct_phase; }; +static const struct dmi_system_id skip_config_led_tbl[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Dell EMC"), + DMI_MATCH(DMI_BOARD_NAME, "0d370eed-89ca-4dc0-a365-e9904c4c62bb"), + }, + }, + {} +}; + static int marvell_read_page(struct phy_device *phydev) { return __phy_read(phydev, MII_MARVELL_PHY_PAGE); @@ -845,6 +856,9 @@ static void marvell_config_led(struct phy_device *phydev) u16 def_config; int err; + if (dmi_check_system(skip_config_led_tbl)) + return; + switch (MARVELL_PHY_FAMILY_ID(phydev->phy_id)) { /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */ case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1121R): From c9aa9d4f49c977e0466c2a17fcb0dfe8bc183fe1 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Mon, 19 Aug 2024 10:59:07 +0800 Subject: [PATCH 378/521] UBUNTU: SAUCE: PCI: ASPM: Allow OS to configure ASPM where BIOS is incapable of BugLink: https://bugs.launchpad.net/bugs/2072679 Since commit f492edb40b54 ("PCI: vmd: Add quirk to configure PCIe ASPM and LTR"), ASPM is configured for NVMe devices enabled in VMD domain. However, that doesn't cover the case when FADT has ACPI_FADT_NO_ASPM set. So add a new attribute to bypass aspm_disabled so OS can configure ASPM. Fixes: f492edb40b54 ("PCI: vmd: Add quirk to configure PCIe ASPM and LTR") Link: https://lore.kernel.org/linux-pm/218aa81f-9c6-5929-578d-8dc15f83dd48@panix.com/ (cherry picked from https://lore.kernel.org/linux-pci/20240530085227.91168-1-kai.heng.feng@canonical.com/) Signed-off-by: Kai-Heng Feng Signed-off-by: Hui Wang Acked-by: Kuan-Ying Lee Acked-by: Aaron Jauregui Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/pci/pcie/aspm.c | 8 ++++++-- include/linux/pci.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 925373b98dff0c..7295d037934fd4 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1472,8 +1472,12 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked) * the _OSC method), we can't honor that request. */ if (aspm_disabled) { - pci_warn(pdev, "can't override BIOS ASPM; OS doesn't have ASPM control\n"); - return -EPERM; + if (aspm_support_enabled && pdev->aspm_os_control) + pci_info(pdev, "BIOS can't program ASPM, let OS control it\n"); + else { + pci_warn(pdev, "can't override BIOS ASPM; OS doesn't have ASPM control\n"); + return -EPERM; + } } if (!locked) diff --git a/include/linux/pci.h b/include/linux/pci.h index 10685bc9fd4c47..2b46163dac4dbb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -511,6 +511,7 @@ struct pci_dev { unsigned int rom_bar_overlap:1; /* ROM BAR disable broken */ unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */ + unsigned int aspm_os_control:1; /* Display of ROM attribute enabled? */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ From 6cef56c262d6d59382fd210378651ff5cfc0a2ac Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Mon, 19 Aug 2024 10:59:08 +0800 Subject: [PATCH 379/521] UBUNTU: SAUCE: PCI: vmd: Let OS control ASPM for devices under VMD domain BugLink: https://bugs.launchpad.net/bugs/2072679 Intel SoC cannot reach lower power states when mapped VMD PCIe bridges and NVMe devices don't have ASPM configured. So set aspm_os_control attribute to let OS really enable ASPM for those devices. Fixes: f492edb40b54 ("PCI: vmd: Add quirk to configure PCIe ASPM and LTR") Link: https://lore.kernel.org/linux-pm/218aa81f-9c6-5929-578d-8dc15f83dd48@panix.com/ (cherry picked from https://lore.kernel.org/linux-pci/20240530085227.91168-2-kai.heng.feng@canonical.com/) Signed-off-by: Kai-Heng Feng Signed-off-by: Hui Wang Acked-by: Kuan-Ying Lee Acked-by: Aaron Jauregui Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/pci/controller/vmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index d4ae250d4bc6d7..3650c6b6038137 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -739,6 +739,8 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata) if (!(features & VMD_FEAT_BIOS_PM_QUIRK)) return 0; + pdev->aspm_os_control = 1; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_LTR); if (!pos) goto out_state_change; From 45822c80052fc50c651deb2aada936132e363a44 Mon Sep 17 00:00:00 2001 From: "Yo-Jung (Leo) Lin" Date: Wed, 3 Sep 2025 15:04:20 -0400 Subject: [PATCH 380/521] UBUNTU: SAUCE: i2c: i801: Do not instantiate spd5118 under SPD Write Disable BugLink: https://bugs.launchpad.net/bugs/2114963 If SPD Write Disable bit in the SMBus is enabled, writing data to addresses from 0x50 to 0x57 is forbidden. This may lead to the following issues for spd5118 devices: 1) Writes to the sensor hwmon sysfs attributes will always result in ENXIO. 2) During system-wide resume, errors may occur during regcache sync, resulting in the following error messages: kernel: spd5118 1-0050: failed to write b = 0: -6 kernel: spd5118 1-0050: pm: dpm_run_callback(): spd5118_resume [spd5118] returns -6 kernel: spd5118 1-0050: pm: failed to resume async: error -6 3) nvmem won't be usable, because writing to the page selector becomes impossible. Also, BIOS vendors may choose to set the page to a value != 0 after a board reset. This will make the sensor not functional unless its MR11 register can be changed, which is impossible due to writes being disabled. To address these issues, don't instantiate it at all if the SPD Write Disable bit is set. Signed-off-by: Yo-Jung Lin (Leo) (cherry picked from https://lore.kernel.org/all/20250613-for-upstream-not-instantiate-spd5118-v2-1-cf456ed9b587@canonical.com/) Signed-off-by: Yo-Jung (Leo) Lin (backported from commit cbb6c74a6507331f0bfd03b33fb763a1005b29ac noble-linux-oem) [cremfuelled: context differed between patch and file, and so a verbatim copy of] [cremfuelled: each hunk was placed where it most faithfully followed the original patch] Signed-off-by: Alice C. Munduruca Acked-by: Chia-Lin Kao (AceLan) Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/i2c/busses/i2c-i801.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index b29c99ed388393..c8e0ebe58e374a 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -988,6 +988,14 @@ static void i801_disable_host_notify(struct i801_priv *priv) iowrite8(priv->original_slvcmd, SMBSLVCMD(priv)); } +static inline __maybe_unused void __i801_register_spd(struct i801_priv *priv) +{ + if (priv->original_hstcfg & SMBHSTCFG_SPD_WD) + i2c_register_spd_write_disable(&priv->adapter); + else + i2c_register_spd_write_enable(&priv->adapter); +} + static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = i801_access, .functionality = i801_func, @@ -1170,6 +1178,19 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap) } } +#ifdef CONFIG_I2C_I801_MUX +static void i801_register_spd(struct i801_priv *priv) +{ + if (!priv->mux_pdev) + __i801_register_spd(priv); +} +#else +static void i801_register_spd(struct i801_priv *priv) +{ + __i801_register_spd(priv); +} +#endif + /* Register optional targets */ static void i801_probe_optional_targets(struct i801_priv *priv) { @@ -1190,10 +1211,7 @@ static void i801_probe_optional_targets(struct i801_priv *priv) dmi_walk(dmi_check_onboard_devices, &priv->adapter); /* Instantiate SPD EEPROMs unless the SMBus is multiplexed */ -#ifdef CONFIG_I2C_I801_MUX - if (!priv->mux_pdev) -#endif - i2c_register_spd_write_enable(&priv->adapter); + i801_register_spd(priv); } #else static void __init input_apanel_init(void) {} @@ -1296,7 +1314,7 @@ static int i801_notifier_call(struct notifier_block *nb, unsigned long action, return NOTIFY_DONE; /* Call i2c_register_spd for muxed child segments */ - i2c_register_spd_write_enable(to_i2c_adapter(dev)); + __i801_register_spd(priv); return NOTIFY_OK; } From 6afbf23327c4fe196e6639ac82811ab0a0293fd4 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 19 Dec 2024 03:38:36 +0800 Subject: [PATCH 381/521] UBUNTU: SAUCE: ACPI: scan: Update HID for new platform BugLink: https://bugs.launchpad.net/bugs/2090932 To resolve the probe-ordering issue on the newer platform Update the HID for LNL and ARL Signed-off-by: Hao Yao Signed-off-by: Jason Chen (cherry picked from commit https://raw.githubusercontent.com/drhooray/vision-drivers/6424af8b225f136375f1a8b01500217d0545c8e6/patch/0001-ACPI-scan-Update-HID-for-new-platform.patch) Signed-off-by: You-Sheng Yang Acked-by: Philip Cox Acked-by: John Cabaj Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/acpi/scan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 530547cda8b286..6aafbf7a2e4dc2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -861,6 +861,8 @@ static const char * const acpi_honor_dep_ids[] = { "INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */ "INTC100A", /* IVSC (RPL) driver must be loaded to allow i2c access to camera sensors */ "INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */ + "INTC10DE", /* IVSC (LNL) driver must be loaded to allow i2c access to camera sensors */ + "INTC10E0", /* IVSC (ARL) driver must be loaded to allow i2c access to camera sensors */ "RSCV0001", /* RISC-V PLIC */ "RSCV0002", /* RISC-V APLIC */ "RSCV0005", /* RISC-V SBI MPXY MBOX */ From 1bd559c221171a527a8e2b12a71d1e0c4d4c033a Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Fri, 27 Mar 2026 17:38:04 +0800 Subject: [PATCH 382/521] UBUNTU: SAUCE: ACPI: respect items already in honor_dep before skipping BugLink: https://bugs.launchpad.net/bugs/2145171 Upstream v6.18-rc1 commit 4405a214df146775338a1e6232701a29024b82e1 "ACPI: scan: Add Intel CVS ACPI HIDs to acpi_ignore_dep_ids[]" (also backported to v6.17.8, v6.12.58), which in effect reverts a not yet upstreamed Intel MIPI camera patch titled "ACPI: scan: Update HID for new platform" meant for bug 2090932, and therefore they share the same symptom: [ 4.484426] ov02c10 i2c-OVTI02C1:00: chip id mismatch: 560243!=0 [ 4.484432] ov02c10 i2c-OVTI02C1:00: failed to find sensor: -6 This patch respects upstream change while keeping Ubuntu's enablement efforts remaining effective. Signed-off-by: You-Sheng Yang Acked-by: Yufeng Gao Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/acpi/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 6aafbf7a2e4dc2..3b0cf49acaed56 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2027,7 +2027,7 @@ int acpi_scan_add_dep(acpi_handle handle, struct acpi_handle_list *dep_devices) honor_dep = acpi_info_matches_ids(info, acpi_honor_dep_ids); kfree(info); - if (skip) + if (skip && !honor_dep) continue; dep = kzalloc_obj(*dep); From 59cc560baecb15d48cb83265862134083814cd98 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Wed, 3 Aug 2022 09:55:00 +0200 Subject: [PATCH 383/521] UBUNTU: SAUCE: igc: wait for the MAC copy when enabled MAC passthrough BugLink: https://bugs.launchpad.net/bugs/1942999 Such as dock hot plug event when runtime, for hardware implementation, the MAC copy takes less than one second when BIOS enabled MAC passthrough. After test on Lenovo TBT4 dock, 600ms is enough to update the MAC address. Otherwise ethernet fails to work. Link: https://lore.kernel.org/lkml/20210702045120.22855-2-aaron.ma@canonical.com/ Signed-off-by: Aaron Ma Acked-by: Stefan Bader [note: this is a non-upstream work-around since upstream does not seem to be to hard at work to do a proper fix] Acked-by: Tim Gardner Signed-off-by: Stefan Bader Signed-off-by: Paolo Pisati Signed-off-by: Mingcong Bai --- drivers/net/ethernet/intel/igc/igc_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 8ac16808023cc6..0249a0bc73df67 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -7202,6 +7202,9 @@ static int igc_probe(struct pci_dev *pdev, memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops)); memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops)); + if (pci_is_thunderbolt_attached(pdev)) + msleep(600); + /* Initialize skew-specific constants */ err = ei->get_invariants(hw); if (err) From e68f6fb9a81b33de57ed6b5fc00a34dafcba0a65 Mon Sep 17 00:00:00 2001 From: Max Lee Date: Wed, 11 Mar 2026 15:51:07 +0800 Subject: [PATCH 384/521] UBUNTU: SAUCE: igc: Increase Thunderbolt MAC passthrough delay to 1000ms BugLink: https://bugs.launchpad.net/bugs/2143197 [Fix] Increase the existing Thunderbolt MAC passthrough wait in igc probe path: from 600ms to 1000ms Ubuntu already carries a SAUCE workaround (commit 534981aaa8) that uses fixed delay timing. Newer Dell docks need a longer timing window. Debug testing shows BIOS MAC update timing: cold boot: MAC already correct at probe start hotplug: MAC becomes correct at ~300-800ms after probe starts Use 1000ms (observed 800ms worst-case + margin) to make hotplug reliable. Upstream context: https://lore.kernel.org/lkml/20210702045120.22855-2-aaron.ma@canonical.com/ (Upstream preferred polling over fixed delay; Ubuntu keeps minimal stable fixed-delay workaround.) Signed-off-by: Max Lee Signed-off-by: Paolo Pisati Signed-off-by: Mingcong Bai --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 0249a0bc73df67..ab567c3eaa92ed 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -7203,7 +7203,7 @@ static int igc_probe(struct pci_dev *pdev, memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops)); if (pci_is_thunderbolt_attached(pdev)) - msleep(600); + msleep(1000); /* Initialize skew-specific constants */ err = ei->get_invariants(hw); From cb24e3dfebc45e33b5677526b144e21b80455486 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Wed, 8 May 2024 18:11:37 +0800 Subject: [PATCH 385/521] DEEPIN: net: stmmac: Add phytium old dwmac acpi_device_id As Phytium ACPI Description Specification document v1.2 p13 Device HID said for old v1.0 Spec,add FTGM0001 for such as some FT2000 device compat. Signed-off-by: Wentao Guan Link: https://github.com/deepin-community/kernel/commit/5c2ef68bfa7f52128c717efb29c3891152fb1c16 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 7df8417da4b623..07614a06be386b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -197,6 +197,7 @@ MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id phytium_dwmac_acpi_ids[] = { + { .id = "FTGM0001" }, // compat FT2000/4 id { .id = "PHYT0004" }, { } }; From edcaeab118bbd24d8fb044789b8af5ef99b05c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=98=E6=80=80?= Date: Tue, 9 Jul 2024 17:17:48 +0800 Subject: [PATCH 386/521] DEEPIN: net: stmmac: dwmac-phytium: compat some FT2000 Compat with some quirk platform FT2000/4 as id "FTGM0001", use rgmii-rxid instead of rgmii (wrong ACPI DSDT), and set dma_ops and host_dma_width (fix code style). origin code in k4.19 is following: acpi_dma_configure(priv->device,DEV_DMA_COHERENT); static u64 my_mask=0xffffffff; priv->device->dma_mask = &my_mask; priv->device->coherent_dma_mask = (u32)~0; port to k5.10 add: pdev->dev.dma_ops = NULL; Signed-off-by: Caicai Signed-off-by: hmy Signed-off-by: wenlunpeng Signed-off-by: Wentao Guan Link: https://github.com/deepin-community/kernel/commit/dbd62afd7ac965ad1637f3b051af98e2362c156d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../ethernet/stmicro/stmmac/dwmac-phytium.c | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 07614a06be386b..8beb560f9aad5c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -68,6 +68,21 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return -ENOMEM; plat->phy_interface = device_get_phy_mode(&pdev->dev); +#ifdef CONFIG_ACPI + static const struct acpi_device_id phytium_old_acpi_id[] = { + { .id = "FTGM0001" }, // compat FT2000/4 id + { } + }; + /* "phy-mode" in phytium platform DSDT is not correct in some old device. + * Force this PHY mode to rgmii-rxid and info of its use. + * If the phy-mode rgmii is realy used, a blacklist may need to be added. + */ + if (acpi_match_device_ids(to_acpi_device(&pdev->dev), phytium_old_acpi_id) && + plat->phy_interface == PHY_INTERFACE_MODE_RGMII) { + plat->phy_interface = PHY_INTERFACE_MODE_RGMII_RXID; + dev_info(&pdev->dev, "phytium workaround: phy-mode from rgmii to rgmii-rxid\n"); + } +#endif if (plat->phy_interface < 0) return plat->phy_interface; @@ -156,7 +171,15 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); plat->dma_cfg->fixed_burst = fwnode_property_read_bool(fwnode, "snps,fixed-burst"); plat->dma_cfg->mixed_burst = fwnode_property_read_bool(fwnode, "snps,mixed-burst"); - +#ifdef CONFIG_ACPI + /* Some old phytium 2000/4 FTGM0001 cannot auto deferred stmmac DMA settings + * show kernel error 'DMA descriptors allocation failed' + */ + if (acpi_match_device_ids(to_acpi_device(&pdev->dev), phytium_old_acpi_id)) { + pdev->dev.dma_ops = NULL; // solved set DMA mask Failed + plat->host_dma_width = 32; + } +#endif plat->axi->axi_lpi_en = false; plat->axi->axi_xit_frm = false; plat->axi->axi_wr_osr_lmt = 7; From b3ad0e7b5e43ad49d9fb0cf7f3a165db2315069e Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 14 Feb 2025 17:48:41 +0800 Subject: [PATCH 387/521] BACKPORT: DEEPIN: pci/quirks: LS7A2000: Fix pm transition of devices under pcie port The CPU may not be able to access the PCI header of the device if it is in low-power mode. Signed-off-by: Zhao Qunqin Signed-off-by: Binbin Zhou Link: https://github.com/deepin-community/kernel/commit/3be8f9c563edaa97e28e3154d3d396b3ba90bf47 [Mingcong Bai: Resolved minor conflict in include/linux/pci.h] Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit [Mingcong Bai: Resolved a minor post-6.19 merge conflict in include/linux/pci.h] Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in include/linux/pci.h ] Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index f1692c782f2dd8..d048a94f2e4981 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -400,6 +400,36 @@ static void quirk_tigerpoint_bm_sts(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGP_LPC, quirk_tigerpoint_bm_sts); #endif +#define DEV_PCIE_PORT_4 0x7a39 +#define DEV_PCIE_PORT_5 0x7a49 +#define DEV_PCIE_PORT_6 0x7a59 +#define DEV_PCIE_PORT_7 0x7a69 +static void loongson_d3_and_link_quirk(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + struct pci_dev *bridge; + static const struct pci_device_id bridge_devids[] = { + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_4) }, + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_5) }, + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_6) }, + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_7) }, + { 0, }, + }; + + /* look for the matching bridge */ + while (!pci_is_root_bus(bus)) { + bridge = bus->self; + bus = bus->parent; + if (bridge && pci_match_id(bridge_devids, bridge)) { + dev->dev_flags |= (PCI_DEV_FLAGS_NO_D3 | + PCI_DEV_FLAGS_NO_LINK_SPEED_CHANGE); + dev->no_d1d2 = 1; + break; + } + } +} +DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_d3_and_link_quirk); + /* Chipsets where PCI->PCI transfers vanish or hang */ static void quirk_nopcipci(struct pci_dev *dev) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 2b46163dac4dbb..c022efb28471bf 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -263,6 +263,7 @@ enum pci_dev_flags { PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS = (__force pci_dev_flags_t) (1 << 14), /* Do not use native PCIe port services (equivalent to pcie_ports=compat) */ PCI_DEV_FLAGS_NO_PORT_SERVICES = (__force pci_dev_flags_t) (1 << 15), + PCI_DEV_FLAGS_NO_LINK_SPEED_CHANGE = (__force pci_dev_flags_t) (1 << 16), }; enum pci_irq_reroute_variant { From ebc4e42d88b90d71a16444244dce8770a49aa826 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 27 Jun 2024 14:37:53 +0800 Subject: [PATCH 388/521] DEEPIN: net: stmmac: fix dwmac-phytium build on 6.9 Declare phytium_dwmac_remove() as a static function to resolve a missing prototype warning. Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/ece68ac12adb566d8ba0c93ae8f3f3abef9e041d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 8beb560f9aad5c..7adeddd82d33c7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -198,7 +198,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -int phytium_dwmac_remove(struct platform_device *pdev) +static int phytium_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); From 5071a4e6227b4669a9fec7e828da171de266ed55 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 28 Jun 2024 11:45:05 +0800 Subject: [PATCH 389/521] DEEPIN: net: ethernet: phytium: fix phytmac_platform on 6.9 Add missing include. Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/02f5b7aec92af6b6cfe6bda39ed1d884b5471606 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 2f2170cf0775ae..ce6271ac52cd08 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -17,6 +17,8 @@ #include "phytmac_v1.h" #include "phytmac_v2.h" +#include + static const struct phytmac_config phytium_1p0_config = { .hw_if = &phytmac_1p0_hw, .caps = PHYTMAC_CAPS_TAILPTR From 951ec5006e406f5394b0d399c65384e285f65c06 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 28 Jun 2024 14:08:40 +0800 Subject: [PATCH 390/521] BACKPORT: DEEPIN: net: ethernet: fix phytmac on 6.9 Declare multiple functions as static functions to suppress warnings about missing prototypes. Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/3672205e780ee88d27b7885e27f450cd332f842c [Kexy: Resolved minor conflicts in drivers/net/ethernet/phytium/phytmac_main.c, and drivers/net/ethernet/phytium/phytmac_v2.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 8 ++++---- drivers/net/ethernet/phytium/phytmac_platform.c | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 1f4e9a2b97ee4f..4ba9e447ac3667 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1852,7 +1852,7 @@ static int phytmac_phylink_connect(struct phytmac *pdata) return 0; } -int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +static int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, const unsigned long *advertising, bool permit_pause_to_mac) @@ -1860,7 +1860,7 @@ int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, return 0; } -void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, +static void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, int speed, int duplex) { struct phytmac *pdata = container_of(pcs, struct phytmac, phylink_pcs); @@ -2010,7 +2010,7 @@ static void phytmac_mac_link_up(struct phylink_config *config, netif_tx_wake_all_queues(ndev); } -int phytmac_mdio_register(struct phytmac *pdata) +static int phytmac_mdio_register(struct phytmac *pdata) { struct phytmac_hw_if *hw_if = pdata->hw_if; int ret; @@ -2652,7 +2652,7 @@ static int phytmac_init(struct phytmac *pdata) return 0; } -void phytmac_default_config(struct phytmac *pdata) +static void phytmac_default_config(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index ce6271ac52cd08..cf628a45172848 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -168,7 +168,6 @@ static int phytmac_get_phy_mode(struct platform_device *pdev) static int phytmac_plat_probe(struct platform_device *pdev) { const struct phytmac_config *phytmac_config = &phytium_1p0_config; - struct device_node *np = pdev->dev.of_node; struct resource *regs; struct phytmac *pdata; int ret, i; From 07585ff65773349b18eea6533a18a648af530e4d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 28 Jun 2024 16:23:20 +0800 Subject: [PATCH 391/521] DEEPIN: net: ethernet: phytium: add a missing declaration for *np MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a compilation error: drivers/net/ethernet/phytium/phytmac_platform.c: In function ‘phytmac_plat_probe’: drivers/net/ethernet/phytium/phytmac_platform.c:98:55: error: ‘np’ undeclared (first use in this function); did you mean ‘up’? 98 | match = of_match_node(phytmac_dt_ids, np); | ^~ | up Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/7b83119ea1c8d2509ad5c8042f05334eddfcc6d9 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index cf628a45172848..d958ca1f168bb2 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -168,6 +168,7 @@ static int phytmac_get_phy_mode(struct platform_device *pdev) static int phytmac_plat_probe(struct platform_device *pdev) { const struct phytmac_config *phytmac_config = &phytium_1p0_config; + struct device_node *np; struct resource *regs; struct phytmac *pdata; int ret, i; From 9afb1158f21f5f40e049672d7e0531ff343ce35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=98=E6=80=80?= Date: Wed, 3 Jul 2024 03:13:45 +0000 Subject: [PATCH 392/521] BACKPORT: DEEPIN: net: phytium: convert and remove validate() references Populate the supported interfaces bitmap and MAC capabilities mask for the phytium driver and remove the old validate implementation. Link:https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=cc0a75eb037539f68f7117a632673a75e39d8d6b Signed-off-by: Wentao Guan Link: https://github.com/deepin-community/kernel-rolling/commit/a5281306bf470a59541210d7e3d7e6636798d348 [Kexy: Resolved minor conflict in drivers/net/ethernet/phytium/phytmac_main.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 71 ++------------------- 1 file changed, 6 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 4ba9e447ac3667..9895b3fccd26e3 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2067,72 +2067,7 @@ static void phytmac_pcs_get_state(struct phylink_config *config, state->link = hw_if->get_link(pdata, state->interface); } -static void phytmac_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state) -{ - struct net_device *ndev = to_net_dev(config->dev); - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - struct phytmac *pdata = netdev_priv(ndev); - - if (state->interface != PHY_INTERFACE_MODE_SGMII && - state->interface != PHY_INTERFACE_MODE_1000BASEX && - state->interface != PHY_INTERFACE_MODE_2500BASEX && - state->interface != PHY_INTERFACE_MODE_5GBASER && - state->interface != PHY_INTERFACE_MODE_10GBASER && - state->interface != PHY_INTERFACE_MODE_USXGMII && - !phy_interface_mode_is_rgmii(state->interface)) { - bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); - return; - } - - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - phylink_set(mask, Asym_Pause); - - if (state->interface == PHY_INTERFACE_MODE_10GBASER || - state->interface == PHY_INTERFACE_MODE_USXGMII) { - pdata->speed = state->speed; - pdata->duplex = state->duplex; - if (pdata->speed == SPEED_5000) { - phylink_set(mask, 5000baseT_Full); - } else { - phylink_set(mask, 10000baseCR_Full); - phylink_set(mask, 10000baseER_Full); - phylink_set(mask, 10000baseKR_Full); - phylink_set(mask, 10000baseLR_Full); - phylink_set(mask, 10000baseLRM_Full); - phylink_set(mask, 10000baseSR_Full); - phylink_set(mask, 10000baseT_Full); - } - } - - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - phylink_set(mask, 2500baseX_Full); - - if (state->interface == PHY_INTERFACE_MODE_5GBASER) - phylink_set(mask, 5000baseT_Full); - - if (state->interface == PHY_INTERFACE_MODE_1000BASEX) - phylink_set(mask, 1000baseX_Full); - - if (state->interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_rgmii(state->interface)) { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseT_Half); - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - } - - bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_and(state->advertising, state->advertising, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); -} - static const struct phylink_mac_ops phytmac_phylink_ops = { - .validate = phytmac_validate, .mac_select_pcs = phytmac_mac_select_pcs, .mac_config = phytmac_mac_config, .mac_link_down = phytmac_mac_link_down, @@ -2145,7 +2080,9 @@ static inline void set_phy_interface(unsigned long *intf) __set_bit(PHY_INTERFACE_MODE_1000BASEX, intf); __set_bit(PHY_INTERFACE_MODE_2500BASEX, intf); __set_bit(PHY_INTERFACE_MODE_USXGMII, intf); + __set_bit(PHY_INTERFACE_MODE_5GBASER, intf); __set_bit(PHY_INTERFACE_MODE_10GBASER, intf); + phy_interface_set_rgmii(intf); } static int phytmac_phylink_create(struct phytmac *pdata) @@ -2167,6 +2104,10 @@ static int phytmac_phylink_create(struct phytmac *pdata) } set_phy_interface(pdata->phylink_config.supported_interfaces); + pdata->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD | + MAC_5000FD | MAC_10000FD; + pdata->phylink = phylink_create(&pdata->phylink_config, fw_node, pdata->phy_interface, &phytmac_phylink_ops); if (IS_ERR(pdata->phylink)) { From adb580401d80d7e06c0a568e55216b6088350a16 Mon Sep 17 00:00:00 2001 From: Ayufan Date: Sun, 30 Dec 2018 13:32:47 +0100 Subject: [PATCH 393/521] ARMBIAN: ayufan: dts: rockpro64: change rx_delay for gmac Change-Id: Ib3899f684188aa1ed1545717af004bba53fe0e07 Signed-off-by: Ayufan Link: https://github.com/armbian/build/blob/dda6950c34f51e167811aee6d0ec220a492aac1a/patch/kernel/archive/rockchip64-6.16/board-rockpro64-change-rx_delay-for-gmac.patch Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi index 8b72ae6449c91a..5f607e83dd44cc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi @@ -300,7 +300,7 @@ snps,reset-active-low; snps,reset-delays-us = <0 10000 50000>; tx_delay = <0x28>; - rx_delay = <0x11>; + rx_delay = <0x20>; status = "okay"; }; From 8e43a16ea7da6b56e035636f0bf233fc26e77ba8 Mon Sep 17 00:00:00 2001 From: The-going <48602507+The-going@users.noreply.github.com> Date: Tue, 1 Feb 2022 19:22:21 +0300 Subject: [PATCH 394/521] ARMBIAN: drv:of: Device Tree Overlay ConfigFS interface Link: https://github.com/armbian/build/blob/bb1753ac3605e7b4988186f7b310f62589b0114e/patch/kernel/archive/sunxi-7.0/patches.armbian/drv-of-add-overlay-configfs-interface.patch Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- drivers/of/Kconfig | 7 + drivers/of/Makefile | 1 + drivers/of/configfs.c | 278 +++++++++++++++++++++++++++++++++++++++ drivers/of/fdt_address.c | 2 +- 4 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 drivers/of/configfs.c diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 50697cc3b07ebe..f9d2fc56fc41c4 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -123,6 +123,13 @@ config OF_OVERLAY_KUNIT_TEST If unsure, say N here, but this option is safe to enable. +config OF_CONFIGFS + bool "Device Tree Overlay ConfigFS interface" + select CONFIGFS_FS + depends on OF_OVERLAY + help + Enable a simple user-space driven DT overlay interface. + config OF_NUMA bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 379a0afcbdc0bf..2be9021017ba5a 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o obj-$(CONFIG_OF_RESOLVE) += resolver.o obj-$(CONFIG_OF_OVERLAY) += overlay.o +obj-$(CONFIG_OF_CONFIGFS) += configfs.o obj-$(CONFIG_OF_NUMA) += of_numa.o ifdef CONFIG_KEXEC_FILE diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c new file mode 100644 index 00000000000000..87974779561fc9 --- /dev/null +++ b/drivers/of/configfs.c @@ -0,0 +1,278 @@ +/* + * Configfs entries for device-tree + * + * Copyright (C) 2013 - Pantelis Antoniou + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "of_private.h" + +struct cfs_overlay_item { + struct config_item item; + + char path[PATH_MAX]; + + const struct firmware *fw; + struct device_node *overlay; + int ov_id; + + void *dtbo; + int dtbo_size; +}; + +static inline struct cfs_overlay_item *to_cfs_overlay_item( + struct config_item *item) +{ + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; +} + +static ssize_t cfs_overlay_item_path_show(struct config_item *item, + char *page) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + return sprintf(page, "%s\n", overlay->path); +} + +static ssize_t cfs_overlay_item_path_store(struct config_item *item, + const char *page, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + const char *p = page; + char *s; + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy to path buffer (and make sure it's always zero terminated */ + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); + overlay->path[sizeof(overlay->path) - 1] = '\0'; + + /* strip trailing newlines */ + s = overlay->path + strlen(overlay->path); + while (s > overlay->path && *--s == '\n') + *s = '\0'; + + pr_debug("%s: path is '%s'\n", __func__, overlay->path); + + err = request_firmware(&overlay->fw, overlay->path, NULL); + if (err != 0) + goto out_err; + + err = of_overlay_fdt_apply((void *)overlay->fw->data, + (u32)overlay->fw->size, + &overlay->ov_id, NULL); + if (err != 0) + goto out_err; + + return count; + +out_err: + + release_firmware(overlay->fw); + overlay->fw = NULL; + + overlay->path[0] = '\0'; + return err; +} + +static ssize_t cfs_overlay_item_status_show(struct config_item *item, + char *page) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + return sprintf(page, "%s\n", + overlay->ov_id > 0 ? "applied" : "unapplied"); +} + +CONFIGFS_ATTR(cfs_overlay_item_, path); +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); + +static struct configfs_attribute *cfs_overlay_attrs[] = { + &cfs_overlay_item_attr_path, + &cfs_overlay_item_attr_status, + NULL, +}; + +ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, + void *buf, size_t max_count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + pr_debug("%s: buf=%p max_count=%zu\n", __func__, + buf, max_count); + + if (overlay->dtbo == NULL) + return 0; + + /* copy if buffer provided */ + if (buf != NULL) { + /* the buffer must be large enough */ + if (overlay->dtbo_size > max_count) + return -ENOSPC; + + memcpy(buf, overlay->dtbo, overlay->dtbo_size); + } + + return overlay->dtbo_size; +} + +ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, + const void *buf, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy the contents */ + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); + if (overlay->dtbo == NULL) + return -ENOMEM; + + overlay->dtbo_size = count; + + err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size, + &overlay->ov_id, NULL); + if (err != 0) + goto out_err; + + return count; + +out_err: + kfree(overlay->dtbo); + overlay->dtbo = NULL; + overlay->dtbo_size = 0; + overlay->ov_id = 0; + + return err; +} + +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); + +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { + &cfs_overlay_item_attr_dtbo, + NULL, +}; + +static void cfs_overlay_release(struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + if (overlay->ov_id > 0) + of_overlay_remove(&overlay->ov_id); + if (overlay->fw) + release_firmware(overlay->fw); + /* kfree with NULL is safe */ + kfree(overlay->dtbo); + kfree(overlay); +} + +static struct configfs_item_operations cfs_overlay_item_ops = { + .release = cfs_overlay_release, +}; + +static struct config_item_type cfs_overlay_type = { + .ct_item_ops = &cfs_overlay_item_ops, + .ct_attrs = cfs_overlay_attrs, + .ct_bin_attrs = cfs_overlay_bin_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *cfs_overlay_group_make_item( + struct config_group *group, const char *name) +{ + struct cfs_overlay_item *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); + return &overlay->item; +} + +static void cfs_overlay_group_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + config_item_put(&overlay->item); +} + +static struct configfs_group_operations overlays_ops = { + .make_item = cfs_overlay_group_make_item, + .drop_item = cfs_overlay_group_drop_item, +}; + +static struct config_item_type overlays_type = { + .ct_group_ops = &overlays_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_group_operations of_cfs_ops = { + /* empty - we don't allow anything to be created */ +}; + +static struct config_item_type of_cfs_type = { + .ct_group_ops = &of_cfs_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group of_cfs_overlay_group; + +static struct configfs_subsystem of_cfs_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "device-tree", + .ci_type = &of_cfs_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), +}; + +static int __init of_cfs_init(void) +{ + int ret; + + pr_info("%s\n", __func__); + + config_group_init(&of_cfs_subsys.su_group); + config_group_init_type_name(&of_cfs_overlay_group, "overlays", + &overlays_type); + configfs_add_default_group(&of_cfs_overlay_group, + &of_cfs_subsys.su_group); + + ret = configfs_register_subsystem(&of_cfs_subsys); + if (ret != 0) { + pr_err("%s: failed to register subsys\n", __func__); + goto out; + } + pr_info("%s: OK\n", __func__); +out: + return ret; +} +late_initcall(of_cfs_init); diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c index f358d2c807540e..e1353d7a58e6ea 100644 --- a/drivers/of/fdt_address.c +++ b/drivers/of/fdt_address.c @@ -147,7 +147,7 @@ static int __init fdt_translate_one(const void *blob, int parent, * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things */ -static u64 __init fdt_translate_address(const void *blob, int node_offset) +u64 __init fdt_translate_address(const void *blob, int node_offset) { int parent, len; const struct of_bus *bus, *pbus; From 015a7694b72160f1ad71225be613cb18103774d9 Mon Sep 17 00:00:00 2001 From: Patrick Yavitz Date: Mon, 23 Dec 2024 10:14:57 -0500 Subject: [PATCH 395/521] ARMBIAN: Enable creation of __symbols__ node Signed-off-by: Patrick Yavitz Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- scripts/Makefile.dtbs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/Makefile.dtbs b/scripts/Makefile.dtbs index c4e4663902844d..8c03bd8a9bc43c 100644 --- a/scripts/Makefile.dtbs +++ b/scripts/Makefile.dtbs @@ -111,6 +111,11 @@ else DTC_FLAGS += -Wunique_unit_address_if_enabled endif +ifeq ($(CONFIG_OF_OVERLAY),y) +# enable creation of __symbols__ node +DTC_FLAGS += -@ +endif + ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),) DTC_FLAGS += -Wnode_name_chars_strict \ -Wproperty_name_chars_strict \ From 92ba214014cb0e834ae15390c51699e6c6bfb8c2 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Tue, 23 Jun 2015 01:26:52 -0500 Subject: [PATCH 396/521] BACKPORT: CLEARLINUX: i8042: decrease debug message level to info Author: Arjan van de Ven Signed-off-by: Miguel Bernal Marin Signed-off-by: Jose Carlos Venegas Munoz [ Mingcong Bai: Debatable, but I think it wouldn't hurt since they were meant for debugging anyway - plus it eliminates 1-2 meaningless errors on LoongArch platforms. ] [ Mingcong Bai: Fixed a minor merge conflict in drivers/input/serio/i8042.c ] Link: https://github.com/clearlinux-pkgs/linux-lts2020/blob/a391d18e5991540961542376a19ff45ac91143e3/0101-i8042-decrease-debug-message-level-to-info.patch Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index c7638b3efc0e7f..316e9506f26171 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -641,7 +641,7 @@ static int i8042_enable_kbd_port(void) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { i8042_ctr &= ~I8042_CTR_KBDINT; i8042_ctr |= I8042_CTR_KBDDIS; - pr_err("Failed to enable KBD port\n"); + pr_info("Failed to enable KBD port\n"); return -EIO; } @@ -660,7 +660,7 @@ static int i8042_enable_aux_port(void) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { i8042_ctr &= ~I8042_CTR_AUXINT; i8042_ctr |= I8042_CTR_AUXDIS; - pr_err("Failed to enable AUX port\n"); + pr_info("Failed to enable AUX port\n"); return -EIO; } @@ -752,7 +752,7 @@ static int i8042_check_mux(void) i8042_ctr &= ~I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - pr_err("Failed to disable AUX port, can't use MUX\n"); + pr_info("Failed to disable AUX port, can't use MUX\n"); return -EIO; } @@ -971,7 +971,7 @@ static int i8042_controller_selftest(void) ret = i8042_command(¶m, I8042_CMD_CTL_TEST); if (ret) - pr_err("i8042 controller selftest timeout (%d/5)\n", i); + pr_info("i8042 controller selftest timeout (%d/5)\n", i); else if (param == I8042_RET_CTL_TEST) return 0; else @@ -994,7 +994,7 @@ static int i8042_controller_selftest(void) pr_info("giving up on controller selftest, continuing anyway...\n"); return 0; #else - pr_err("i8042 controller selftest failed\n"); + pr_info("i8042 controller selftest failed\n"); return -EIO; #endif } From 722aedf613fde8b3a059e70a22f3dfcbff71ae72 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 18 Oct 2020 16:42:44 +0900 Subject: [PATCH 397/521] SURFACE: (surface3-oemb) add DMI matches for Surface 3 with broken DMI table On some Surface 3, the DMI table gets corrupted for unknown reasons and breaks existing DMI matching used for device-specific quirks. This commit adds the (broken) DMI data into dmi_system_id tables used for quirks so that each driver can enable quirks even on the affected systems. On affected systems, DMI data will look like this: $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ chassis_vendor,product_name,sys_vendor} /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. /sys/devices/virtual/dmi/id/board_name:OEMB /sys/devices/virtual/dmi/id/board_vendor:OEMB /sys/devices/virtual/dmi/id/chassis_vendor:OEMB /sys/devices/virtual/dmi/id/product_name:OEMB /sys/devices/virtual/dmi/id/sys_vendor:OEMB Expected: $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ chassis_vendor,product_name,sys_vendor} /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. /sys/devices/virtual/dmi/id/board_name:Surface 3 /sys/devices/virtual/dmi/id/board_vendor:Microsoft Corporation /sys/devices/virtual/dmi/id/chassis_vendor:Microsoft Corporation /sys/devices/virtual/dmi/id/product_name:Surface 3 /sys/devices/virtual/dmi/id/sys_vendor:Microsoft Corporation Signed-off-by: Tsuchiya Yuto Patchset: surface3 Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/surface3-wmi.c | 7 +++++++ sound/soc/codecs/rt5645.c | 9 +++++++++ sound/soc/intel/common/soc-acpi-intel-cht-match.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c index 6c8fb7a4dde444..22797a53f4d8f1 100644 --- a/drivers/platform/surface/surface3-wmi.c +++ b/drivers/platform/surface/surface3-wmi.c @@ -37,6 +37,13 @@ static const struct dmi_system_id surface3_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), }, }, + { + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), + DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), + }, + }, #endif { } }; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index f7701b8d0d3c7e..0708c2fe8f6102 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3793,6 +3793,15 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&intel_braswell_platform_data, }, + { + .ident = "Microsoft Surface 3", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), + DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, { /* * Match for the GPDwin which unfortunately uses somewhat diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index e4c3492a0c2824..0b930c91bccb33 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -27,6 +27,14 @@ static const struct dmi_system_id cht_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), }, }, + { + .callback = cht_surface_quirk_cb, + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), + DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), + }, + }, { } }; From 1f3d628086eb04c7aafc4a8bfca7e4536a44fa22 Mon Sep 17 00:00:00 2001 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> Date: Fri, 6 Dec 2019 23:10:30 +0900 Subject: [PATCH 398/521] SURFACE: surface3-spi: workaround: disable DMA mode to avoid crash by default On Arch Linux kernel at least after 4.19, touch input is broken after suspend (s2idle). kern :err : [ +0.203408] Surface3-spi spi-MSHW0037:00: SPI transfer timed out On recent stable Arch Linux kernel (at least after 5.1), touch input will crash after the first touch. kern :err : [ +0.203592] Surface3-spi spi-MSHW0037:00: SPI transfer timed out kern :err : [ +0.000173] spi_master spi1: failed to transfer one message from queue I found on an affected system (Arch Linux kernel, etc.), the touchscreen driver uses DMA mode by default. Then, we found some kernels with different kernel config (5.1 kernel config from Jakeday [1] or Chromium OS kernel chromeos-4.19 [2]) will use PIO mode by default and no such issues there. So, this commit disables DMA mode on the touchscreen driver side as a quick workaround to avoid touch input crash. We may need to properly set up DMA mode to use the touchscreen driver with DMA mode. You can still switch DMA/PIO mode if you want: switch to DMA mode (maybe broken) echo 1 | sudo tee /sys/module/surface3_spi/parameters/use_dma back to PIO mode echo 0 | sudo tee /sys/module/surface3_spi/parameters/use_dma Link to issue: https://github.com/jakeday/linux-surface/issues/596 References: [1] https://github.com/jakeday/linux-surface/blob/master/configs/5.1/config [2] https://chromium.googlesource.com/chromiumos/third_party/kernel/+/refs/heads/chromeos-4.19 Tested on Arch Linux 5.4.1 with Surface 3, which will use DMA by default. This commit made the driver use PIO by default and no touch input crash. Also tested on chromeos-4.19 4.19.90 with Surface 3, which will use PIO by default even without this commit. After this commit, it still uses PIO and confirmed no functional changes regarding touch input. More details: We can confirm which mode the touchscreen driver uses; first, enable debug output: echo "file drivers/spi/spi-pxa2xx.c +p" | sudo tee /sys/kernel/debug/dynamic_debug/control echo "file drivers/input/touchscreen/surface3_spi.c +p" | sudo tee /sys/kernel/debug/dynamic_debug/control Then, try to make a touch input and see dmesg log (On Arch Linux kernel, uses DMA) kern :debug : [ +0.006383] Surface3-spi spi-MSHW0037:00: 7692307 Hz actual, DMA kern :debug : [ +0.000495] Surface3-spi spi-MSHW0037:00: surface3_spi_irq_handler received -> ff ff ff ff a5 5a e7 7e 01 d2 00 80 01 03 03 18 00 e4 01 00 04 1a 04 1a e3 0c e3 0c b0 00 c5 00 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff (On the kernels I referenced above, uses PIO) kern :debug : [ +0.009260] Surface3-spi spi-MSHW0037:00: 7692307 Hz actual, PIO kern :debug : [ +0.001105] Surface3-spi spi-MSHW0037:00: surface3_spi_irq_handler received -> ff ff ff ff a5 5a e7 7e 01 d2 00 80 01 03 03 24 00 e4 01 00 58 0b 58 0b 83 12 83 12 26 01 95 01 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff Note (2025-03-08): This patch was originally dropped due to the comments in [3]. However, according to the commments in [4] it is still required. [3]: https://github.com/linux-surface/kernel/commit/a3421c12bed0e46c28518bcb8c6b22f237c6dc7a [4]: https://github.com/linux-surface/linux-surface/issues/1184 Patchset: surface3 Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/input/touchscreen/surface3_spi.c | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c index 6074b7730e8621..6aa3e1d6f160a8 100644 --- a/drivers/input/touchscreen/surface3_spi.c +++ b/drivers/input/touchscreen/surface3_spi.c @@ -25,6 +25,12 @@ #define SURFACE3_REPORT_TOUCH 0xd2 #define SURFACE3_REPORT_PEN 0x16 +bool use_dma = false; +module_param(use_dma, bool, 0644); +MODULE_PARM_DESC(use_dma, + "Disable DMA mode if you encounter touch input crash. " + "(default: false, disabled to avoid crash)"); + struct surface3_ts_data { struct spi_device *spi; struct gpio_desc *gpiod_rst[2]; @@ -317,6 +323,13 @@ static int surface3_spi_create_pen_input(struct surface3_ts_data *data) return 0; } +static bool surface3_spi_can_dma(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + return use_dma; +} + static int surface3_spi_probe(struct spi_device *spi) { struct surface3_ts_data *data; @@ -359,6 +372,19 @@ static int surface3_spi_probe(struct spi_device *spi) if (error) return error; + /* + * Set up DMA + * + * TODO: Currently, touch input with DMA seems to be broken. + * On 4.19 LTS, touch input will crash after suspend. + * On recent stable kernel (at least after 5.1), touch input will crash after + * the first touch. No problem with PIO on those kernels. + * Maybe we need to configure DMA here. + * + * Link to issue: https://github.com/jakeday/linux-surface/issues/596 + */ + spi->controller->can_dma = surface3_spi_can_dma; + return 0; } From b77e8b13c0dc64a4024f20055400cbe9cd09fe26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 3 Nov 2020 13:28:04 +0100 Subject: [PATCH 399/521] SURFACE: mwifiex: Add quirk resetting the PCI bridge on MS Surface devices The most recent firmware of the 88W8897 card reports a hardcoded LTR value to the system during initialization, probably as an (unsuccessful) attempt of the developers to fix firmware crashes. This LTR value prevents most of the Microsoft Surface devices from entering deep powersaving states (either platform C-State 10 or S0ix state), because the exit latency of that state would be higher than what the card can tolerate. Turns out the card works just the same (including the firmware crashes) no matter if that hardcoded LTR value is reported or not, so it's kind of useless and only prevents us from saving power. To get rid of those hardcoded LTR reports, it's possible to reset the PCI bridge device after initializing the cards firmware. I'm not exactly sure why that works, maybe the power management subsystem of the PCH resets its stored LTR values when doing a function level reset of the bridge device. Doing the reset once after starting the wifi firmware works very well, probably because the firmware only reports that LTR value a single time during firmware startup. Patchset: mwifiex Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/net/wireless/marvell/mwifiex/pcie.c | 12 +++++++++ .../wireless/marvell/mwifiex/pcie_quirks.c | 26 +++++++++++++------ .../wireless/marvell/mwifiex/pcie_quirks.h | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index a760de191fce73..db9b203226a89a 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1702,9 +1702,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) static void mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; + struct pci_dev *pdev = card->dev; + struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; + /* Trigger a function level reset of the PCI bridge device, this makes + * the firmware of PCIe 88W8897 cards stop reporting a fixed LTR value + * that prevents the system from entering package C10 and S0ix powersaving + * states. + * We need to do it here because it must happen after firmware + * initialization and this function is called after that is done. + */ + if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) + pci_reset_function(parent_pdev); + /* Write the RX ring read pointer in to reg->rx_rdptr */ mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | tx_wrap); } diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c index dd6d21f1dbfd77..f46b06f8d64353 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c @@ -13,7 +13,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Pro 5", @@ -22,7 +23,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Pro 5 (LTE)", @@ -31,7 +33,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Pro 6", @@ -39,7 +42,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Book 1", @@ -47,7 +51,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Book 2", @@ -55,7 +60,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Laptop 1", @@ -63,7 +69,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Laptop 2", @@ -71,7 +78,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, {} }; @@ -89,6 +97,8 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) dev_info(&pdev->dev, "no quirks enabled\n"); if (card->quirks & QUIRK_FW_RST_D3COLD) dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); + if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) + dev_info(&pdev->dev, "quirk do_flr_on_bridge enabled\n"); } static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h index d6ff964aec5bfd..5d30ae39d65ec6 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h @@ -4,6 +4,7 @@ #include "pcie.h" #define QUIRK_FW_RST_D3COLD BIT(0) +#define QUIRK_DO_FLR_ON_BRIDGE BIT(1) void mwifiex_initialize_quirks(struct pcie_service_card *card); int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); From 13b8a0f6c1449c2999956158c1a16c5859cf1a4c Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 4 Oct 2020 00:11:49 +0900 Subject: [PATCH 400/521] SURFACE: mwifiex: pcie: disable bridge_d3 for Surface gen4+ Currently, mwifiex fw will crash after suspend on recent kernel series. On Windows, it seems that the root port of wifi will never enter D3 state (stay on D0 state). And on Linux, disabling the D3 state for the bridge fixes fw crashing after suspend. This commit disables the D3 state of root port on driver initialization and fixes fw crashing after suspend. Signed-off-by: Tsuchiya Yuto Patchset: mwifiex Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/net/wireless/marvell/mwifiex/pcie.c | 7 +++++ .../wireless/marvell/mwifiex/pcie_quirks.c | 27 +++++++++++++------ .../wireless/marvell/mwifiex/pcie_quirks.h | 1 + 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index db9b203226a89a..ffd0c1fe922399 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -377,6 +377,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pcie_service_card *card; + struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); int ret; pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", @@ -418,6 +419,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, return -1; } + /* disable bridge_d3 for Surface gen4+ devices to fix fw crashing + * after suspend + */ + if (card->quirks & QUIRK_NO_BRIDGE_D3) + parent_pdev->bridge_d3 = false; + return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c index f46b06f8d64353..99b024ecbadeaf 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c @@ -14,7 +14,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Pro 5", @@ -24,7 +25,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Pro 5 (LTE)", @@ -34,7 +36,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Pro 6", @@ -43,7 +46,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Book 1", @@ -52,7 +56,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Book 2", @@ -61,7 +66,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Laptop 1", @@ -70,7 +76,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Laptop 2", @@ -79,7 +86,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, {} }; @@ -99,6 +107,9 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) dev_info(&pdev->dev, "quirk do_flr_on_bridge enabled\n"); + if (card->quirks & QUIRK_NO_BRIDGE_D3) + dev_info(&pdev->dev, + "quirk no_brigde_d3 enabled\n"); } static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h index 5d30ae39d65ec6..c14eb56eb9118b 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h @@ -5,6 +5,7 @@ #define QUIRK_FW_RST_D3COLD BIT(0) #define QUIRK_DO_FLR_ON_BRIDGE BIT(1) +#define QUIRK_NO_BRIDGE_D3 BIT(2) void mwifiex_initialize_quirks(struct pcie_service_card *card); int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); From 06f6ad52110e3f07abb08dcb271dd73204084ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Thu, 25 Mar 2021 11:33:02 +0100 Subject: [PATCH 401/521] SURFACE: Bluetooth: btusb: Lower passive lescan interval on Marvell 88W8897 The Marvell 88W8897 combined wifi and bluetooth card (pcie+usb version) is used in a lot of Microsoft Surface devices, and all those devices suffer from very low 2.4GHz wifi connection speeds while bluetooth is enabled. The reason for that is that the default passive scanning interval for Bluetooth Low Energy devices is quite high in Linux (interval of 60 msec and scan window of 30 msec, see hci_core.c), and the Marvell chip is known for its bad bt+wifi coexisting performance. So decrease that passive scan interval and make the scan window shorter on this particular device to allow for spending more time transmitting wifi signals: The new scan interval is 250 msec (0x190 * 0.625 msec) and the new scan window is 6.25 msec (0xa * 0,625 msec). This change has a very large impact on the 2.4GHz wifi speeds and gets it up to performance comparable with the Windows driver, which seems to apply a similar quirk. The interval and window length were tested and found to work very well with a lot of Bluetooth Low Energy devices, including the Surface Pen, a Bluetooth Speaker and two modern Bluetooth headphones. All devices were discovered immediately after turning them on. Even lower values were also tested, but they introduced longer delays until devices get discovered. Patchset: mwifiex Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/bluetooth/btusb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index cc6392f8d98cd2..8f6db6c76a248b 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -67,6 +67,7 @@ static struct usb_driver btusb_driver; #define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26) #define BTUSB_ACTIONS_SEMI BIT(27) #define BTUSB_BARROT BIT(28) +#define BTUSB_LOWER_LESCAN_INTERVAL BIT(29) static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -472,6 +473,7 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL }, { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL }, { USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL }, + { USB_DEVICE(0x1286, 0x204c), .driver_info = BTUSB_LOWER_LESCAN_INTERVAL }, /* Intel Bluetooth devices */ { USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED }, @@ -4248,6 +4250,19 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL) hdev->set_bdaddr = btusb_set_bdaddr_marvell; + /* The Marvell 88W8897 combined wifi and bluetooth card is known for + * very bad bt+wifi coexisting performance. + * + * Decrease the passive BT Low Energy scan interval a bit + * (0x0190 * 0.625 msec = 250 msec) and make the scan window shorter + * (0x000a * 0,625 msec = 6.25 msec). This allows for significantly + * higher wifi throughput while passively scanning for BT LE devices. + */ + if (id->driver_info & BTUSB_LOWER_LESCAN_INTERVAL) { + hdev->le_scan_interval = 0x0190; + hdev->le_scan_window = 0x000a; + } + if (IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK) && (id->driver_info & BTUSB_MEDIATEK)) { hdev->setup = btusb_mtk_setup; From b5fa9a34e350fda73364cdefa344eef1dd886708 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Feb 2021 00:45:52 +0100 Subject: [PATCH 402/521] SURFACE: ath10k: Add module parameters to override board files Some Surface devices, specifically the Surface Go and AMD version of the Surface Laptop 3 (wich both come with QCA6174 WiFi chips), work better with a different board file, as it seems that the firmeware included upstream is buggy. As it is generally not a good idea to randomly overwrite files, let alone doing so via packages, we add module parameters to override those file names in the driver. This allows us to package/deploy the override via a modprobe.d config. Signed-off-by: Maximilian Luz Patchset: ath10k Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/net/wireless/ath/ath10k/core.c | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7c2939cbde5f05..eb402a3a8c5f88 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -41,6 +41,9 @@ static bool fw_diag_log; /* frame mode values are mapped as per enum ath10k_hw_txrx_mode */ unsigned int ath10k_frame_mode = ATH10K_HW_TXRX_NATIVE_WIFI; +static char *override_board = ""; +static char *override_board2 = ""; + unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) | BIT(ATH10K_FW_CRASH_DUMP_CE_DATA); @@ -53,6 +56,9 @@ module_param(fw_diag_log, bool, 0644); module_param_named(frame_mode, ath10k_frame_mode, uint, 0644); module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444); +module_param(override_board, charp, 0644); +module_param(override_board2, charp, 0644); + MODULE_PARM_DESC(debug_mask, "Debugging mask"); MODULE_PARM_DESC(uart_print, "Uart target debugging"); MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); @@ -62,6 +68,9 @@ MODULE_PARM_DESC(frame_mode, MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file"); MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging"); +MODULE_PARM_DESC(override_board, "Override for board.bin file"); +MODULE_PARM_DESC(override_board2, "Override for board-2.bin file"); + static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .id = QCA988X_HW_2_0_VERSION, @@ -932,6 +941,42 @@ static int ath10k_init_configure_target(struct ath10k *ar) return 0; } +static const char *ath10k_override_board_fw_file(struct ath10k *ar, + const char *file) +{ + if (strcmp(file, "board.bin") == 0) { + if (strcmp(override_board, "") == 0) + return file; + + if (strcmp(override_board, "none") == 0) { + dev_info(ar->dev, "firmware override: pretending 'board.bin' does not exist\n"); + return NULL; + } + + dev_info(ar->dev, "firmware override: replacing 'board.bin' with '%s'\n", + override_board); + + return override_board; + } + + if (strcmp(file, "board-2.bin") == 0) { + if (strcmp(override_board2, "") == 0) + return file; + + if (strcmp(override_board2, "none") == 0) { + dev_info(ar->dev, "firmware override: pretending 'board-2.bin' does not exist\n"); + return NULL; + } + + dev_info(ar->dev, "firmware override: replacing 'board-2.bin' with '%s'\n", + override_board2); + + return override_board2; + } + + return file; +} + static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, const char *dir, const char *file) @@ -946,6 +991,18 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, if (dir == NULL) dir = "."; + /* HACK: Override board.bin and board-2.bin files if specified. + * + * Some Surface devices perform better with a different board + * configuration. To this end, one would need to replace the board.bin + * file with the modified config and remove the board-2.bin file. + * Unfortunately, that's not a solution that we can easily package. So + * we add module options to perform these overrides here. + */ + file = ath10k_override_board_fw_file(ar, file); + if (!file) + return ERR_PTR(-ENOENT); + if (ar->board_name) { snprintf(filename, sizeof(filename), "%s/%s/%s", dir, ar->board_name, file); From 2e93cee1897744384e2ae4557990e1f83967eead Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 30 Jul 2020 13:21:53 +0200 Subject: [PATCH 403/521] SURFACE: mei: me: Add Icelake device ID for iTouch Signed-off-by: Dorian Stoll Patchset: ipts Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/misc/mei/hw-me-regs.h | 4 ++++ drivers/misc/mei/pci-me.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index f145e8e36cb3d0..0436998e8126d1 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -94,6 +94,10 @@ #define PCI_DEVICE_ID_INTEL_MEI_ICP_LP 0x34E0 /* Ice Lake Point LP */ #define PCI_DEVICE_ID_INTEL_MEI_ICP_N 0x38E0 /* Ice Lake Point N */ +#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ +#define MEI_DEV_ID_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ +#define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ + #define PCI_DEVICE_ID_INTEL_MEI_JSP_N 0x4DE0 /* Jasper Lake Point N */ #define PCI_DEVICE_ID_INTEL_MEI_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 9efeafa8f1cae9..d97945f5b8683b 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -100,6 +100,10 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP_3, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, From b6263fd474011a07c5a4b2427430168496f525a9 Mon Sep 17 00:00:00 2001 From: Liban Hannan Date: Tue, 12 Apr 2022 23:31:12 +0100 Subject: [PATCH 404/521] BACKPORT: SURFACE: iommu: Use IOMMU passthrough mode for IPTS Adds a quirk so that IOMMU uses passthrough mode for the IPTS device. Otherwise, when IOMMU is enabled, IPTS produces DMAR errors like: DMAR: [DMA Read NO_PASID] Request device [00:16.4] fault addr 0x104ea3000 [fault reason 0x06] PTE Read access is not set This is very similar to the bug described at: https://bugs.launchpad.net/bugs/1958004 Fixed with the following patch which this patch basically copies: https://launchpadlibrarian.net/586396847/43255ca.diff Signed-off-by: Dorian Stoll Patchset: ipts [ Mingcong Bai: Resolved a minor merge conflict in drivers/iommu/intel/iommu.c ] Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index d473c445eecf20..b0428bf4269b94 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -47,6 +47,11 @@ (pdev)->device == 0x1919)) #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) +#define IS_IPTS(pdev) ( \ + ((pdev)->vendor == PCI_VENDOR_ID_INTEL && (pdev)->device == 0x9D3E) || \ + ((pdev)->vendor == PCI_VENDOR_ID_INTEL && (pdev)->device == 0x34E4) \ + ) + #define IOAPIC_RANGE_START (0xfee00000) #define IOAPIC_RANGE_END (0xfeefffff) #define IOVA_START_ADDR (0x1000) @@ -211,6 +216,7 @@ int intel_iommu_enabled = 0; EXPORT_SYMBOL_GPL(intel_iommu_enabled); static int dmar_map_ipu = 1; +static int dmar_map_ipts = 1; static int intel_iommu_superpage = 1; static int iommu_identity_mapping; static int iommu_skip_te_disable; @@ -218,6 +224,7 @@ static int disable_igfx_iommu; #define IDENTMAP_AZALIA 4 #define IDENTMAP_IPU 8 +#define IDENTMAP_IPTS 16 const struct iommu_ops intel_iommu_ops; @@ -1411,6 +1418,9 @@ static int device_def_domain_type(struct device *dev) if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev)) return IOMMU_DOMAIN_IDENTITY; + + if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev)) + return IOMMU_DOMAIN_IDENTITY; } return 0; @@ -1704,6 +1714,9 @@ static int __init init_dmars(void) if (!dmar_map_ipu) iommu_identity_mapping |= IDENTMAP_IPU; + if (!dmar_map_ipts) + iommu_identity_mapping |= IDENTMAP_IPTS; + check_tylersburg_isoch(); /* @@ -4063,6 +4076,22 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9B41, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCA, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCC, quirk_iommu_igfx); +static void quirk_iommu_ipts(struct pci_dev *dev) +{ + if (!IS_IPTS(dev)) + return; + + if (risky_device(dev)) + return; + + pci_info(dev, "Disabling IOMMU for IPTS\n"); + dmar_map_ipts = 0; +} + +/* disable IPTS dmar support */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x34E4, quirk_iommu_ipts); + static void quirk_iommu_ipu(struct pci_dev *dev) { if (!IS_INTEL_IPU(dev)) From 8a36cb2321ff7077b28badf45f1517c200edab63 Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Sun, 11 Dec 2022 12:00:59 +0100 Subject: [PATCH 405/521] SURFACE: hid: Add support for Intel Precise Touch and Stylus Based on linux-surface/intel-precise-touch@8abe268 Signed-off-by: Dorian Stoll Patchset: ipts Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 2 + drivers/hid/ipts/Kconfig | 14 + drivers/hid/ipts/Makefile | 16 ++ drivers/hid/ipts/cmd.c | 61 +++++ drivers/hid/ipts/cmd.h | 60 ++++ drivers/hid/ipts/context.h | 52 ++++ drivers/hid/ipts/control.c | 486 +++++++++++++++++++++++++++++++++ drivers/hid/ipts/control.h | 126 +++++++++ drivers/hid/ipts/desc.h | 80 ++++++ drivers/hid/ipts/eds1.c | 104 +++++++ drivers/hid/ipts/eds1.h | 35 +++ drivers/hid/ipts/eds2.c | 145 ++++++++++ drivers/hid/ipts/eds2.h | 35 +++ drivers/hid/ipts/hid.c | 225 +++++++++++++++ drivers/hid/ipts/hid.h | 24 ++ drivers/hid/ipts/main.c | 126 +++++++++ drivers/hid/ipts/mei.c | 188 +++++++++++++ drivers/hid/ipts/mei.h | 66 +++++ drivers/hid/ipts/receiver.c | 251 +++++++++++++++++ drivers/hid/ipts/receiver.h | 16 ++ drivers/hid/ipts/resources.c | 131 +++++++++ drivers/hid/ipts/resources.h | 41 +++ drivers/hid/ipts/spec-data.h | 100 +++++++ drivers/hid/ipts/spec-device.h | 290 ++++++++++++++++++++ drivers/hid/ipts/spec-hid.h | 34 +++ drivers/hid/ipts/thread.c | 84 ++++++ drivers/hid/ipts/thread.h | 59 ++++ 28 files changed, 2853 insertions(+) create mode 100644 drivers/hid/ipts/Kconfig create mode 100644 drivers/hid/ipts/Makefile create mode 100644 drivers/hid/ipts/cmd.c create mode 100644 drivers/hid/ipts/cmd.h create mode 100644 drivers/hid/ipts/context.h create mode 100644 drivers/hid/ipts/control.c create mode 100644 drivers/hid/ipts/control.h create mode 100644 drivers/hid/ipts/desc.h create mode 100644 drivers/hid/ipts/eds1.c create mode 100644 drivers/hid/ipts/eds1.h create mode 100644 drivers/hid/ipts/eds2.c create mode 100644 drivers/hid/ipts/eds2.h create mode 100644 drivers/hid/ipts/hid.c create mode 100644 drivers/hid/ipts/hid.h create mode 100644 drivers/hid/ipts/main.c create mode 100644 drivers/hid/ipts/mei.c create mode 100644 drivers/hid/ipts/mei.h create mode 100644 drivers/hid/ipts/receiver.c create mode 100644 drivers/hid/ipts/receiver.h create mode 100644 drivers/hid/ipts/resources.c create mode 100644 drivers/hid/ipts/resources.h create mode 100644 drivers/hid/ipts/spec-data.h create mode 100644 drivers/hid/ipts/spec-device.h create mode 100644 drivers/hid/ipts/spec-hid.h create mode 100644 drivers/hid/ipts/thread.c create mode 100644 drivers/hid/ipts/thread.h diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ff2f580b660ba6..cecb264a229f99 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1466,6 +1466,8 @@ source "drivers/hid/surface-hid/Kconfig" source "drivers/hid/intel-thc-hid/Kconfig" +source "drivers/hid/ipts/Kconfig" + endif # HID # USB support may be used with HID disabled diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0597fd6a4ffd32..b46dec8ad0bfbb 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -180,3 +180,5 @@ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ + +obj-$(CONFIG_HID_IPTS) += ipts/ diff --git a/drivers/hid/ipts/Kconfig b/drivers/hid/ipts/Kconfig new file mode 100644 index 00000000000000..297401bd388dd9 --- /dev/null +++ b/drivers/hid/ipts/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config HID_IPTS + tristate "Intel Precise Touch & Stylus" + depends on INTEL_MEI + depends on HID + help + Say Y here if your system has a touchscreen using Intels + Precise Touch & Stylus (IPTS) technology. + + If unsure say N. + + To compile this driver as a module, choose M here: the + module will be called ipts. diff --git a/drivers/hid/ipts/Makefile b/drivers/hid/ipts/Makefile new file mode 100644 index 00000000000000..883896f68e6ada --- /dev/null +++ b/drivers/hid/ipts/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for the IPTS touchscreen driver +# + +obj-$(CONFIG_HID_IPTS) += ipts.o +ipts-objs := cmd.o +ipts-objs += control.o +ipts-objs += eds1.o +ipts-objs += eds2.o +ipts-objs += hid.o +ipts-objs += main.o +ipts-objs += mei.o +ipts-objs += receiver.o +ipts-objs += resources.o +ipts-objs += thread.o diff --git a/drivers/hid/ipts/cmd.c b/drivers/hid/ipts/cmd.c new file mode 100644 index 00000000000000..63a4934bbc5fae --- /dev/null +++ b/drivers/hid/ipts/cmd.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include + +#include "cmd.h" +#include "context.h" +#include "mei.h" +#include "spec-device.h" + +int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code, + struct ipts_response *rsp, u64 timeout) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!rsp) + return -EFAULT; + + /* + * In a response, the command code will have the most significant bit flipped to 1. + * If code is passed to ipts_mei_recv as is, no messages will be received. + */ + ret = ipts_mei_recv(&ipts->mei, code | IPTS_RSP_BIT, rsp, timeout); + if (ret < 0) + return ret; + + dev_dbg(ipts->dev, "Received 0x%02X with status 0x%02X\n", code, rsp->status); + + /* + * Some devices will always return this error. + * It is allowed to ignore it and to try continuing. + */ + if (rsp->status == IPTS_STATUS_COMPAT_CHECK_FAIL) + rsp->status = IPTS_STATUS_SUCCESS; + + return 0; +} + +int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size) +{ + struct ipts_command cmd = { 0 }; + + if (!ipts) + return -EFAULT; + + cmd.cmd = code; + + if (data && size > 0) + memcpy(cmd.payload, data, size); + + dev_dbg(ipts->dev, "Sending 0x%02X with %ld bytes payload\n", code, size); + return ipts_mei_send(&ipts->mei, &cmd, sizeof(cmd.cmd) + size); +} diff --git a/drivers/hid/ipts/cmd.h b/drivers/hid/ipts/cmd.h new file mode 100644 index 00000000000000..2b4079075b6428 --- /dev/null +++ b/drivers/hid/ipts/cmd.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_CMD_H +#define IPTS_CMD_H + +#include + +#include "context.h" +#include "spec-device.h" + +/* + * The default timeout for receiving responses + */ +#define IPTS_CMD_DEFAULT_TIMEOUT 1000 + +/** + * ipts_cmd_recv_timeout() - Receives a response to a command. + * @ipts: The IPTS driver context. + * @code: The type of the command / response. + * @rsp: The address that the received response will be copied to. + * @timeout: How many milliseconds the function will wait at most. + * + * A negative timeout means to wait forever. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received. + */ +int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code, + struct ipts_response *rsp, u64 timeout); + +/** + * ipts_cmd_recv() - Receives a response to a command. + * @ipts: The IPTS driver context. + * @code: The type of the command / response. + * @rsp: The address that the received response will be copied to. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received. + */ +static inline int ipts_cmd_recv(struct ipts_context *ipts, enum ipts_command_code code, + struct ipts_response *rsp) +{ + return ipts_cmd_recv_timeout(ipts, code, rsp, IPTS_CMD_DEFAULT_TIMEOUT); +} + +/** + * ipts_cmd_send() - Executes a command on the device. + * @ipts: The IPTS driver context. + * @code: The type of the command to execute. + * @data: The payload containing parameters for the command. + * @size: The size of the payload. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size); + +#endif /* IPTS_CMD_H */ diff --git a/drivers/hid/ipts/context.h b/drivers/hid/ipts/context.h new file mode 100644 index 00000000000000..ba33259f1f7c5e --- /dev/null +++ b/drivers/hid/ipts/context.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_CONTEXT_H +#define IPTS_CONTEXT_H + +#include +#include +#include +#include +#include +#include +#include + +#include "mei.h" +#include "resources.h" +#include "spec-device.h" +#include "thread.h" + +struct ipts_context { + struct device *dev; + struct ipts_mei mei; + + enum ipts_mode mode; + + /* + * Prevents concurrent GET_FEATURE reports. + */ + struct mutex feature_lock; + struct completion feature_event; + + /* + * These are not inside of struct ipts_resources + * because they don't own the memory they point to. + */ + struct ipts_buffer feature_report; + struct ipts_buffer descriptor; + + bool hid_active; + struct hid_device *hid; + + struct ipts_device_info info; + struct ipts_resources resources; + + struct ipts_thread receiver_loop; +}; + +#endif /* IPTS_CONTEXT_H */ diff --git a/drivers/hid/ipts/control.c b/drivers/hid/ipts/control.c new file mode 100644 index 00000000000000..5360842d260bae --- /dev/null +++ b/drivers/hid/ipts/control.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "context.h" +#include "control.h" +#include "desc.h" +#include "hid.h" +#include "receiver.h" +#include "resources.h" +#include "spec-data.h" +#include "spec-device.h" + +static int ipts_control_get_device_info(struct ipts_context *ipts, struct ipts_device_info *info) +{ + int ret = 0; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!info) + return -EFAULT; + + ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0); + if (ret) { + dev_err(ipts->dev, "GET_DEVICE_INFO: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DEVICE_INFO, &rsp); + if (ret) { + dev_err(ipts->dev, "GET_DEVICE_INFO: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "GET_DEVICE_INFO: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + memcpy(info, rsp.payload, sizeof(*info)); + return 0; +} + +static int ipts_control_set_mode(struct ipts_context *ipts, enum ipts_mode mode) +{ + int ret = 0; + struct ipts_set_mode cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + cmd.mode = mode; + + ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MODE, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "SET_MODE: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MODE, &rsp); + if (ret) { + dev_err(ipts->dev, "SET_MODE: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "SET_MODE: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +static int ipts_control_set_mem_window(struct ipts_context *ipts, struct ipts_resources *res) +{ + int i = 0; + int ret = 0; + struct ipts_mem_window cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!res) + return -EFAULT; + + for (i = 0; i < IPTS_BUFFERS; i++) { + cmd.data_addr_lower[i] = lower_32_bits(res->data[i].dma_address); + cmd.data_addr_upper[i] = upper_32_bits(res->data[i].dma_address); + cmd.feedback_addr_lower[i] = lower_32_bits(res->feedback[i].dma_address); + cmd.feedback_addr_upper[i] = upper_32_bits(res->feedback[i].dma_address); + } + + cmd.workqueue_addr_lower = lower_32_bits(res->workqueue.dma_address); + cmd.workqueue_addr_upper = upper_32_bits(res->workqueue.dma_address); + + cmd.doorbell_addr_lower = lower_32_bits(res->doorbell.dma_address); + cmd.doorbell_addr_upper = upper_32_bits(res->doorbell.dma_address); + + cmd.hid2me_addr_lower = lower_32_bits(res->hid2me.dma_address); + cmd.hid2me_addr_upper = upper_32_bits(res->hid2me.dma_address); + + cmd.workqueue_size = IPTS_WORKQUEUE_SIZE; + cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE; + + ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MEM_WINDOW, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "SET_MEM_WINDOW: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MEM_WINDOW, &rsp); + if (ret) { + dev_err(ipts->dev, "SET_MEM_WINDOW: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "SET_MEM_WINDOW: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +static int ipts_control_get_descriptor(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_data_header *header = NULL; + struct ipts_get_descriptor cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!ipts->resources.descriptor.address) + return -EFAULT; + + memset(ipts->resources.descriptor.address, 0, ipts->resources.descriptor.size); + + cmd.addr_lower = lower_32_bits(ipts->resources.descriptor.dma_address); + cmd.addr_upper = upper_32_bits(ipts->resources.descriptor.dma_address); + cmd.magic = 8; + + ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DESCRIPTOR, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "GET_DESCRIPTOR: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DESCRIPTOR, &rsp); + if (ret) { + dev_err(ipts->dev, "GET_DESCRIPTOR: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "GET_DESCRIPTOR: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + header = (struct ipts_data_header *)ipts->resources.descriptor.address; + + if (header->type == IPTS_DATA_TYPE_DESCRIPTOR) { + ipts->descriptor.address = &header->data[8]; + ipts->descriptor.size = header->size - 8; + + return 0; + } + + return -ENODATA; +} + +int ipts_control_request_flush(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_quiesce_io cmd = { 0 }; + + if (!ipts) + return -EFAULT; + + ret = ipts_cmd_send(ipts, IPTS_CMD_QUIESCE_IO, &cmd, sizeof(cmd)); + if (ret) + dev_err(ipts->dev, "QUIESCE_IO: send failed: %d\n", ret); + + return ret; +} + +int ipts_control_wait_flush(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + ret = ipts_cmd_recv(ipts, IPTS_CMD_QUIESCE_IO, &rsp); + if (ret) { + dev_err(ipts->dev, "QUIESCE_IO: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status == IPTS_STATUS_TIMEOUT) + return -EAGAIN; + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "QUIESCE_IO: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +int ipts_control_request_data(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + ret = ipts_cmd_send(ipts, IPTS_CMD_READY_FOR_DATA, NULL, 0); + if (ret) + dev_err(ipts->dev, "READY_FOR_DATA: send failed: %d\n", ret); + + return ret; +} + +int ipts_control_wait_data(struct ipts_context *ipts, bool shutdown) +{ + int ret = 0; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!shutdown) + ret = ipts_cmd_recv_timeout(ipts, IPTS_CMD_READY_FOR_DATA, &rsp, 0); + else + ret = ipts_cmd_recv(ipts, IPTS_CMD_READY_FOR_DATA, &rsp); + + if (ret) { + if (ret != -EAGAIN) + dev_err(ipts->dev, "READY_FOR_DATA: recv failed: %d\n", ret); + + return ret; + } + + /* + * During shutdown, it is possible that the sensor has already been disabled. + */ + if (rsp.status == IPTS_STATUS_SENSOR_DISABLED) + return 0; + + if (rsp.status == IPTS_STATUS_TIMEOUT) + return -EAGAIN; + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "READY_FOR_DATA: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer) +{ + int ret = 0; + struct ipts_feedback cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + cmd.buffer = buffer; + + ret = ipts_cmd_send(ipts, IPTS_CMD_FEEDBACK, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "FEEDBACK: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_FEEDBACK, &rsp); + if (ret) { + dev_err(ipts->dev, "FEEDBACK: recv failed: %d\n", ret); + return ret; + } + + /* + * We don't know what feedback data looks like so we are sending zeros. + * See also ipts_control_refill_buffer. + */ + if (rsp.status == IPTS_STATUS_INVALID_PARAMS) + return 0; + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "FEEDBACK: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd, + enum ipts_feedback_data_type type, void *data, size_t size) +{ + struct ipts_feedback_header *header = NULL; + + if (!ipts) + return -EFAULT; + + if (!ipts->resources.hid2me.address) + return -EFAULT; + + memset(ipts->resources.hid2me.address, 0, ipts->resources.hid2me.size); + header = (struct ipts_feedback_header *)ipts->resources.hid2me.address; + + header->cmd_type = cmd; + header->data_type = type; + header->size = size; + header->buffer = IPTS_HID2ME_BUFFER; + + if (size + sizeof(*header) > ipts->resources.hid2me.size) + return -EINVAL; + + if (data && size > 0) + memcpy(header->payload, data, size); + + return ipts_control_send_feedback(ipts, IPTS_HID2ME_BUFFER); +} + +int ipts_control_start(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_device_info info = { 0 }; + + if (!ipts) + return -EFAULT; + + dev_info(ipts->dev, "Starting IPTS\n"); + + ret = ipts_control_get_device_info(ipts, &info); + if (ret) { + dev_err(ipts->dev, "Failed to get device info: %d\n", ret); + return ret; + } + + ipts->info = info; + + ret = ipts_resources_init(&ipts->resources, ipts->dev, info.data_size, info.feedback_size); + if (ret) { + dev_err(ipts->dev, "Failed to allocate buffers: %d", ret); + return ret; + } + + dev_info(ipts->dev, "IPTS EDS Version: %d\n", info.intf_eds); + + /* + * Handle newer devices + */ + if (info.intf_eds > 1) { + /* + * Fetching the descriptor will only work on newer devices. + * For older devices, a fallback descriptor will be used. + */ + ret = ipts_control_get_descriptor(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to fetch HID descriptor: %d\n", ret); + return ret; + } + + /* + * Newer devices can be directly initialized in polling mode. + */ + ipts->mode = IPTS_MODE_POLL; + } + + ret = ipts_control_set_mode(ipts, ipts->mode); + if (ret) { + dev_err(ipts->dev, "Failed to set mode: %d\n", ret); + return ret; + } + + ret = ipts_control_set_mem_window(ipts, &ipts->resources); + if (ret) { + dev_err(ipts->dev, "Failed to set memory window: %d\n", ret); + return ret; + } + + ret = ipts_receiver_start(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to start receiver: %d\n", ret); + return ret; + } + + ret = ipts_control_request_data(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to request data: %d\n", ret); + return ret; + } + + ipts_hid_enable(ipts); + + ret = ipts_hid_init(ipts, info); + if (ret) { + dev_err(ipts->dev, "Failed to initialize HID device: %d\n", ret); + return ret; + } + + return 0; +} + +static int _ipts_control_stop(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + ipts_hid_disable(ipts); + dev_info(ipts->dev, "Stopping IPTS\n"); + + ret = ipts_receiver_stop(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to stop receiver: %d\n", ret); + return ret; + } + + ret = ipts_resources_free(&ipts->resources); + if (ret) { + dev_err(ipts->dev, "Failed to free resources: %d\n", ret); + return ret; + } + + return 0; +} + +int ipts_control_stop(struct ipts_context *ipts) +{ + int ret = 0; + + ret = _ipts_control_stop(ipts); + if (ret) + return ret; + + ret = ipts_hid_free(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to free HID device: %d\n", ret); + return ret; + } + + return 0; +} + +int ipts_control_restart(struct ipts_context *ipts) +{ + int ret = 0; + + ret = _ipts_control_stop(ipts); + if (ret) + return ret; + + /* + * Wait a second to give the sensor time to fully shut down. + */ + msleep(1000); + + ret = ipts_control_start(ipts); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/hid/ipts/control.h b/drivers/hid/ipts/control.h new file mode 100644 index 00000000000000..26629c5144edb7 --- /dev/null +++ b/drivers/hid/ipts/control.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_CONTROL_H +#define IPTS_CONTROL_H + +#include + +#include "context.h" +#include "spec-data.h" +#include "spec-device.h" + +/** + * ipts_control_request_flush() - Stop the data flow. + * @ipts: The IPTS driver context. + * + * Runs the command to stop the data flow on the device. + * All outstanding data needs to be acknowledged using feedback before the command will return. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_request_flush(struct ipts_context *ipts); + +/** + * ipts_control_wait_flush() - Wait until data flow has been stopped. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_wait_flush(struct ipts_context *ipts); + +/** + * ipts_control_wait_flush() - Notify the device that the driver can receive new data. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_request_data(struct ipts_context *ipts); + +/** + * ipts_control_wait_data() - Wait until new data is available. + * @ipts: The IPTS driver context. + * @block: Whether to block execution until data is available. + * + * In poll mode, this function will never return while the data flow is active. Instead, + * the poll will be incremented when new data is available. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no data is available. + */ +int ipts_control_wait_data(struct ipts_context *ipts, bool block); + +/** + * ipts_control_send_feedback() - Submits a feedback buffer to the device. + * @ipts: The IPTS driver context. + * @buffer: The ID of the buffer containing feedback data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer); + +/** + * ipts_control_hid2me_feedback() - Sends HID2ME feedback, a special type of feedback. + * @ipts: The IPTS driver context. + * @cmd: The command that will be run on the device. + * @type: The type of the payload that is sent to the device. + * @data: The payload of the feedback command. + * @size: The size of the payload. + * + * HID2ME feedback is a special type of feedback, because it allows interfacing with + * the HID API of the device at any moment, without requiring a buffer that has to + * be acknowledged. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd, + enum ipts_feedback_data_type type, void *data, size_t size); + +/** + * ipts_control_refill_buffer() - Acknowledges that data in a buffer has been processed. + * @ipts: The IPTS driver context. + * @buffer: The buffer that has been processed and can be refilled. + * + * Returns: 0 on success, <0 on error. + */ +static inline int ipts_control_refill_buffer(struct ipts_context *ipts, u32 buffer) +{ + /* + * IPTS expects structured data in the feedback buffer matching the buffer that will be + * refilled. We don't know what that data looks like, so we just keep the buffer empty. + * This results in an INVALID_PARAMS error, but the buffer gets refilled without an issue. + * Sending a minimal structure with the buffer ID fixes the error, but breaks refilling + * the buffers on some devices. + */ + + return ipts_control_send_feedback(ipts, buffer); +} + +/** + * ipts_control_start() - Initialized the device and starts the data flow. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_start(struct ipts_context *ipts); + +/** + * ipts_control_stop() - Stops the data flow and resets the device. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_stop(struct ipts_context *ipts); + +/** + * ipts_control_restart() - Stops the device and starts it again. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_restart(struct ipts_context *ipts); + +#endif /* IPTS_CONTROL_H */ diff --git a/drivers/hid/ipts/desc.h b/drivers/hid/ipts/desc.h new file mode 100644 index 00000000000000..307438c7c80cd7 --- /dev/null +++ b/drivers/hid/ipts/desc.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_DESC_H +#define IPTS_DESC_H + +#include + +#define IPTS_HID_REPORT_SINGLETOUCH 64 +#define IPTS_HID_REPORT_DATA 65 +#define IPTS_HID_REPORT_SET_MODE 66 + +#define IPTS_HID_REPORT_DATA_SIZE 7485 + +/* + * HID descriptor for singletouch data. + * This descriptor should be present on all IPTS devices. + */ +static const u8 ipts_singletouch_descriptor[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x04, /* Usage (Touchscreen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x40, /* Report ID (64), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x07, /* Report Count (7), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x55, 0x0E, /* Unit Exponent (14), */ + 0x65, 0x11, /* Unit (Centimeter), */ + 0x46, 0x76, 0x0B, /* Physical Maximum (2934), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x74, 0x06, /* Physical Maximum (1652), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0xC0, /* End Collection */ +}; + +/* + * Fallback HID descriptor for older devices that do not have + * the ability to query their HID descriptor. + */ +static const u8 ipts_fallback_descriptor[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x0F, /* Usage (Capacitive Hm Digitizer), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x41, /* Report ID (65), */ + 0x09, 0x56, /* Usage (Scan Time), */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x10, /* Report Size (16), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x61, /* Usage (Gesture Char Quality), */ + 0x75, 0x08, /* Report Size (8), */ + 0x96, 0x3D, 0x1D, /* Report Count (7485), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x85, 0x42, /* Report ID (66), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0xC8, /* Usage (C8h), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ +}; + +#endif /* IPTS_DESC_H */ diff --git a/drivers/hid/ipts/eds1.c b/drivers/hid/ipts/eds1.c new file mode 100644 index 00000000000000..7b9f54388a9f68 --- /dev/null +++ b/drivers/hid/ipts/eds1.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include + +#include "context.h" +#include "control.h" +#include "desc.h" +#include "eds1.h" +#include "spec-device.h" + +int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) +{ + size_t size = 0; + u8 *buffer = NULL; + + if (!ipts) + return -EFAULT; + + if (!desc_buffer) + return -EFAULT; + + if (!desc_size) + return -EFAULT; + + size = sizeof(ipts_singletouch_descriptor) + sizeof(ipts_fallback_descriptor); + + buffer = kzalloc(size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor)); + memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts_fallback_descriptor, + sizeof(ipts_fallback_descriptor)); + + *desc_size = size; + *desc_buffer = buffer; + + return 0; +} + +static int ipts_eds1_switch_mode(struct ipts_context *ipts, enum ipts_mode mode) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (ipts->mode == mode) + return 0; + + ipts->mode = mode; + + ret = ipts_control_restart(ipts); + if (ret) + dev_err(ipts->dev, "Failed to switch modes: %d\n", ret); + + return ret; +} + +int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + if (report_id != IPTS_HID_REPORT_SET_MODE) + return -EIO; + + if (report_type != HID_FEATURE_REPORT) + return -EIO; + + if (size != 2) + return -EINVAL; + + /* + * Implement mode switching report for older devices without native HID support. + */ + + if (request_type == HID_REQ_GET_REPORT) { + memset(buffer, 0, size); + buffer[0] = report_id; + buffer[1] = ipts->mode; + } else if (request_type == HID_REQ_SET_REPORT) { + return ipts_eds1_switch_mode(ipts, buffer[1]); + } else { + return -EIO; + } + + return ret; +} diff --git a/drivers/hid/ipts/eds1.h b/drivers/hid/ipts/eds1.h new file mode 100644 index 00000000000000..eeeb6575e3e89f --- /dev/null +++ b/drivers/hid/ipts/eds1.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include + +#include "context.h" + +/** + * ipts_eds1_get_descriptor() - Assembles the HID descriptor of the device. + * @ipts: The IPTS driver context. + * @desc_buffer: A pointer to the location where the address of the allocated buffer is stored. + * @desc_size: A pointer to the location where the size of the allocated buffer is stored. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size); + +/** + * ipts_eds1_raw_request() - Executes an output or feature report on the device. + * @ipts: The IPTS driver context. + * @buffer: The buffer containing the report. + * @size: The size of the buffer. + * @report_id: The HID report ID. + * @report_type: Whether this report is an output or a feature report. + * @request_type: Whether this report requests or sends data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type); diff --git a/drivers/hid/ipts/eds2.c b/drivers/hid/ipts/eds2.c new file mode 100644 index 00000000000000..639940794615df --- /dev/null +++ b/drivers/hid/ipts/eds2.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "control.h" +#include "desc.h" +#include "eds2.h" +#include "spec-data.h" + +int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) +{ + size_t size = 0; + u8 *buffer = NULL; + + if (!ipts) + return -EFAULT; + + if (!desc_buffer) + return -EFAULT; + + if (!desc_size) + return -EFAULT; + + size = sizeof(ipts_singletouch_descriptor) + ipts->descriptor.size; + + buffer = kzalloc(size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor)); + memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts->descriptor.address, + ipts->descriptor.size); + + *desc_size = size; + *desc_buffer = buffer; + + return 0; +} + +static int ipts_eds2_get_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum ipts_feedback_data_type type) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + mutex_lock(&ipts->feature_lock); + + memset(buffer, 0, size); + buffer[0] = report_id; + + memset(&ipts->feature_report, 0, sizeof(ipts->feature_report)); + reinit_completion(&ipts->feature_event); + + ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size); + if (ret) { + dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret); + goto out; + } + + ret = wait_for_completion_timeout(&ipts->feature_event, msecs_to_jiffies(5000)); + if (ret == 0) { + dev_warn(ipts->dev, "GET_FEATURES timed out!\n"); + ret = -EIO; + goto out; + } + + if (!ipts->feature_report.address) { + ret = -EFAULT; + goto out; + } + + if (ipts->feature_report.size > size) { + ret = -ETOOSMALL; + goto out; + } + + ret = ipts->feature_report.size; + memcpy(buffer, ipts->feature_report.address, ipts->feature_report.size); + +out: + mutex_unlock(&ipts->feature_lock); + return ret; +} + +static int ipts_eds2_set_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum ipts_feedback_data_type type) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + buffer[0] = report_id; + + ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size); + if (ret) + dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret); + + return ret; +} + +int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type) +{ + enum ipts_feedback_data_type feedback_type = IPTS_FEEDBACK_DATA_TYPE_VENDOR; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + if (report_type == HID_OUTPUT_REPORT && request_type == HID_REQ_SET_REPORT) + feedback_type = IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; + else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_GET_REPORT) + feedback_type = IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES; + else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_SET_REPORT) + feedback_type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES; + else + return -EIO; + + if (request_type == HID_REQ_GET_REPORT) + return ipts_eds2_get_feature(ipts, buffer, size, report_id, feedback_type); + else + return ipts_eds2_set_feature(ipts, buffer, size, report_id, feedback_type); +} diff --git a/drivers/hid/ipts/eds2.h b/drivers/hid/ipts/eds2.h new file mode 100644 index 00000000000000..064e3716907ab5 --- /dev/null +++ b/drivers/hid/ipts/eds2.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include + +#include "context.h" + +/** + * ipts_eds2_get_descriptor() - Assembles the HID descriptor of the device. + * @ipts: The IPTS driver context. + * @desc_buffer: A pointer to the location where the address of the allocated buffer is stored. + * @desc_size: A pointer to the location where the size of the allocated buffer is stored. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size); + +/** + * ipts_eds2_raw_request() - Executes an output or feature report on the device. + * @ipts: The IPTS driver context. + * @buffer: The buffer containing the report. + * @size: The size of the buffer. + * @report_id: The HID report ID. + * @report_type: Whether this report is an output or a feature report. + * @request_type: Whether this report requests or sends data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type); diff --git a/drivers/hid/ipts/hid.c b/drivers/hid/ipts/hid.c new file mode 100644 index 00000000000000..e34a1a4f9fa779 --- /dev/null +++ b/drivers/hid/ipts/hid.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "desc.h" +#include "eds1.h" +#include "eds2.h" +#include "hid.h" +#include "spec-data.h" +#include "spec-hid.h" + +void ipts_hid_enable(struct ipts_context *ipts) +{ + WRITE_ONCE(ipts->hid_active, true); +} + +void ipts_hid_disable(struct ipts_context *ipts) +{ + WRITE_ONCE(ipts->hid_active, false); +} + +static int ipts_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void ipts_hid_stop(struct hid_device *hid) +{ +} + +static int ipts_hid_parse(struct hid_device *hid) +{ + int ret = 0; + struct ipts_context *ipts = NULL; + + u8 *buffer = NULL; + size_t size = 0; + + if (!hid) + return -ENODEV; + + ipts = hid->driver_data; + + if (!ipts) + return -EFAULT; + + if (!READ_ONCE(ipts->hid_active)) + return -ENODEV; + + if (ipts->info.intf_eds == 1) + ret = ipts_eds1_get_descriptor(ipts, &buffer, &size); + else + ret = ipts_eds2_get_descriptor(ipts, &buffer, &size); + + if (ret) { + dev_err(ipts->dev, "Failed to allocate HID descriptor: %d\n", ret); + return ret; + } + + ret = hid_parse_report(hid, buffer, size); + kfree(buffer); + + if (ret) { + dev_err(ipts->dev, "Failed to parse HID descriptor: %d\n", ret); + return ret; + } + + return 0; +} + +static int ipts_hid_raw_request(struct hid_device *hid, unsigned char report_id, __u8 *buffer, + size_t size, unsigned char report_type, int request_type) +{ + struct ipts_context *ipts = NULL; + + if (!hid) + return -ENODEV; + + ipts = hid->driver_data; + + if (!ipts) + return -EFAULT; + + if (!READ_ONCE(ipts->hid_active)) + return -ENODEV; + + if (ipts->info.intf_eds == 1) { + return ipts_eds1_raw_request(ipts, buffer, size, report_id, report_type, + request_type); + } else { + return ipts_eds2_raw_request(ipts, buffer, size, report_id, report_type, + request_type); + } +} + +static struct hid_ll_driver ipts_hid_driver = { + .start = ipts_hid_start, + .stop = ipts_hid_stop, + .open = ipts_hid_start, + .close = ipts_hid_stop, + .parse = ipts_hid_parse, + .raw_request = ipts_hid_raw_request, +}; + +int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer) +{ + u8 *temp = NULL; + struct ipts_hid_header *frame = NULL; + struct ipts_data_header *header = NULL; + + if (!ipts) + return -EFAULT; + + if (!ipts->hid) + return -ENODEV; + + if (!READ_ONCE(ipts->hid_active)) + return -ENODEV; + + header = (struct ipts_data_header *)ipts->resources.data[buffer].address; + + temp = ipts->resources.report.address; + memset(temp, 0, ipts->resources.report.size); + + if (!header) + return -EFAULT; + + if (header->size == 0) + return 0; + + if (header->type == IPTS_DATA_TYPE_HID) + return hid_input_report(ipts->hid, HID_INPUT_REPORT, header->data, header->size, 1); + + if (header->type == IPTS_DATA_TYPE_GET_FEATURES) { + ipts->feature_report.address = header->data; + ipts->feature_report.size = header->size; + + complete_all(&ipts->feature_event); + return 0; + } + + if (header->type != IPTS_DATA_TYPE_FRAME) + return 0; + + if (header->size + 3 + sizeof(struct ipts_hid_header) > IPTS_HID_REPORT_DATA_SIZE) + return -ERANGE; + + /* + * Synthesize a HID report matching the devices that natively send HID reports + */ + temp[0] = IPTS_HID_REPORT_DATA; + + frame = (struct ipts_hid_header *)&temp[3]; + frame->type = IPTS_HID_FRAME_TYPE_RAW; + frame->size = header->size + sizeof(*frame); + + memcpy(frame->data, header->data, header->size); + + return hid_input_report(ipts->hid, HID_INPUT_REPORT, temp, IPTS_HID_REPORT_DATA_SIZE, 1); +} + +int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (ipts->hid) + return 0; + + ipts->hid = hid_allocate_device(); + if (IS_ERR(ipts->hid)) { + int err = PTR_ERR(ipts->hid); + + dev_err(ipts->dev, "Failed to allocate HID device: %d\n", err); + return err; + } + + ipts->hid->driver_data = ipts; + ipts->hid->dev.parent = ipts->dev; + ipts->hid->ll_driver = &ipts_hid_driver; + + ipts->hid->vendor = info.vendor; + ipts->hid->product = info.product; + ipts->hid->group = HID_GROUP_GENERIC; + + snprintf(ipts->hid->name, sizeof(ipts->hid->name), "IPTS %04X:%04X", info.vendor, + info.product); + + ret = hid_add_device(ipts->hid); + if (ret) { + dev_err(ipts->dev, "Failed to add HID device: %d\n", ret); + ipts_hid_free(ipts); + return ret; + } + + return 0; +} + +int ipts_hid_free(struct ipts_context *ipts) +{ + if (!ipts) + return -EFAULT; + + if (!ipts->hid) + return 0; + + hid_destroy_device(ipts->hid); + ipts->hid = NULL; + + return 0; +} diff --git a/drivers/hid/ipts/hid.h b/drivers/hid/ipts/hid.h new file mode 100644 index 00000000000000..1ebe77447903ad --- /dev/null +++ b/drivers/hid/ipts/hid.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_HID_H +#define IPTS_HID_H + +#include + +#include "context.h" +#include "spec-device.h" + +void ipts_hid_enable(struct ipts_context *ipts); +void ipts_hid_disable(struct ipts_context *ipts); + +int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer); + +int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info); +int ipts_hid_free(struct ipts_context *ipts); + +#endif /* IPTS_HID_H */ diff --git a/drivers/hid/ipts/main.c b/drivers/hid/ipts/main.c new file mode 100644 index 00000000000000..fb5b5c13ee3eaa --- /dev/null +++ b/drivers/hid/ipts/main.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "control.h" +#include "mei.h" +#include "receiver.h" +#include "spec-device.h" + +/* + * The MEI client ID for IPTS functionality. + */ +#define IPTS_ID UUID_LE(0x3e8d0870, 0x271a, 0x4208, 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) + +static int ipts_set_dma_mask(struct mei_cl_device *cldev) +{ + if (!cldev) + return -EFAULT; + + if (!dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64))) + return 0; + + return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32)); +} + +static int ipts_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) +{ + int ret = 0; + struct ipts_context *ipts = NULL; + + if (!cldev) + return -EFAULT; + + ret = ipts_set_dma_mask(cldev); + if (ret) { + dev_err(&cldev->dev, "Failed to set DMA mask for IPTS: %d\n", ret); + return ret; + } + + ret = mei_cldev_enable(cldev); + if (ret) { + dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret); + return ret; + } + + ipts = devm_kzalloc(&cldev->dev, sizeof(*ipts), GFP_KERNEL); + if (!ipts) { + mei_cldev_disable(cldev); + return -ENOMEM; + } + + ret = ipts_mei_init(&ipts->mei, cldev); + if (ret) { + dev_err(&cldev->dev, "Failed to init MEI bus logic: %d\n", ret); + return ret; + } + + ipts->dev = &cldev->dev; + ipts->mode = IPTS_MODE_EVENT; + + mutex_init(&ipts->feature_lock); + init_completion(&ipts->feature_event); + + mei_cldev_set_drvdata(cldev, ipts); + + ret = ipts_control_start(ipts); + if (ret) { + dev_err(&cldev->dev, "Failed to start IPTS: %d\n", ret); + return ret; + } + + return 0; +} + +static void ipts_remove(struct mei_cl_device *cldev) +{ + int ret = 0; + struct ipts_context *ipts = NULL; + + if (!cldev) { + pr_err("MEI device is NULL!"); + return; + } + + ipts = mei_cldev_get_drvdata(cldev); + + ret = ipts_control_stop(ipts); + if (ret) + dev_err(&cldev->dev, "Failed to stop IPTS: %d\n", ret); + + mei_cldev_disable(cldev); +} + +static struct mei_cl_device_id ipts_device_id_table[] = { + { .uuid = IPTS_ID, .version = MEI_CL_VERSION_ANY }, + {}, +}; +MODULE_DEVICE_TABLE(mei, ipts_device_id_table); + +static struct mei_cl_driver ipts_driver = { + .id_table = ipts_device_id_table, + .name = "ipts", + .probe = ipts_probe, + .remove = ipts_remove, +}; +module_mei_cl_driver(ipts_driver); + +MODULE_DESCRIPTION("IPTS touchscreen driver"); +MODULE_AUTHOR("Dorian Stoll "); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/ipts/mei.c b/drivers/hid/ipts/mei.c new file mode 100644 index 00000000000000..1e0395ceae4a4b --- /dev/null +++ b/drivers/hid/ipts/mei.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "mei.h" + +static void locked_list_add(struct list_head *new, struct list_head *head, + struct rw_semaphore *lock) +{ + down_write(lock); + list_add(new, head); + up_write(lock); +} + +static void locked_list_del(struct list_head *entry, struct rw_semaphore *lock) +{ + down_write(lock); + list_del(entry); + up_write(lock); +} + +static void ipts_mei_incoming(struct mei_cl_device *cldev) +{ + ssize_t ret = 0; + struct ipts_mei_message *entry = NULL; + struct ipts_context *ipts = NULL; + + if (!cldev) { + pr_err("MEI device is NULL!"); + return; + } + + ipts = mei_cldev_get_drvdata(cldev); + if (!ipts) { + pr_err("IPTS driver context is NULL!"); + return; + } + + entry = devm_kzalloc(ipts->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + INIT_LIST_HEAD(&entry->list); + + do { + ret = mei_cldev_recv(cldev, (u8 *)&entry->rsp, sizeof(entry->rsp)); + } while (ret == -EINTR); + + if (ret < 0) { + dev_err(ipts->dev, "Error while reading response: %ld\n", ret); + return; + } + + if (ret == 0) { + dev_err(ipts->dev, "Received empty response\n"); + return; + } + + locked_list_add(&entry->list, &ipts->mei.messages, &ipts->mei.message_lock); + wake_up_all(&ipts->mei.message_queue); +} + +static int ipts_mei_search(struct ipts_mei *mei, enum ipts_command_code code, + struct ipts_response *rsp) +{ + struct ipts_mei_message *entry = NULL; + + if (!mei) + return -EFAULT; + + if (!rsp) + return -EFAULT; + + down_read(&mei->message_lock); + + /* + * Iterate over the list of received messages, and check if there is one + * matching the requested command code. + */ + list_for_each_entry(entry, &mei->messages, list) { + if (entry->rsp.cmd == code) + break; + } + + up_read(&mei->message_lock); + + /* + * If entry is not the list head, this means that the loop above has been stopped early, + * and that we found a matching element. We drop the message from the list and return it. + */ + if (!list_entry_is_head(entry, &mei->messages, list)) { + locked_list_del(&entry->list, &mei->message_lock); + + *rsp = entry->rsp; + devm_kfree(&mei->cldev->dev, entry); + + return 0; + } + + return -EAGAIN; +} + +int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp, + u64 timeout) +{ + int ret = 0; + + if (!mei) + return -EFAULT; + + /* + * A timeout of 0 means check and return immideately. + */ + if (timeout == 0) + return ipts_mei_search(mei, code, rsp); + + /* + * A timeout of less than 0 means to wait forever. + */ + if (timeout < 0) { + wait_event(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0); + return 0; + } + + ret = wait_event_timeout(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0, + msecs_to_jiffies(timeout)); + + if (ret > 0) + return 0; + + return -EAGAIN; +} + +int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length) +{ + int ret = 0; + + if (!mei) + return -EFAULT; + + if (!mei->cldev) + return -EFAULT; + + if (!data) + return -EFAULT; + + do { + ret = mei_cldev_send(mei->cldev, (u8 *)data, length); + } while (ret == -EINTR); + + if (ret < 0) + return ret; + + return 0; +} + +int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev) +{ + if (!mei) + return -EFAULT; + + if (!cldev) + return -EFAULT; + + mei->cldev = cldev; + + INIT_LIST_HEAD(&mei->messages); + init_waitqueue_head(&mei->message_queue); + init_rwsem(&mei->message_lock); + + mei_cldev_register_rx_cb(cldev, ipts_mei_incoming); + + return 0; +} diff --git a/drivers/hid/ipts/mei.h b/drivers/hid/ipts/mei.h new file mode 100644 index 00000000000000..973bade6b0fdd1 --- /dev/null +++ b/drivers/hid/ipts/mei.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_MEI_H +#define IPTS_MEI_H + +#include +#include +#include +#include +#include + +#include "spec-device.h" + +struct ipts_mei_message { + struct list_head list; + struct ipts_response rsp; +}; + +struct ipts_mei { + struct mei_cl_device *cldev; + + struct list_head messages; + + wait_queue_head_t message_queue; + struct rw_semaphore message_lock; +}; + +/** + * ipts_mei_recv() - Receive data from a MEI device. + * @mei: The IPTS MEI device context. + * @code: The IPTS command code to look for. + * @rsp: The address that the received data will be copied to. + * @timeout: How many milliseconds the function will wait at most. + * + * A negative timeout means to wait forever. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received. + */ +int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp, + u64 timeout); + +/** + * ipts_mei_send() - Send data to a MEI device. + * @ipts: The IPTS MEI device context. + * @data: The data to send. + * @size: The size of the data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length); + +/** + * ipts_mei_init() - Initialize the MEI device context. + * @mei: The MEI device context to initialize. + * @cldev: The MEI device the context will be bound to. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev); + +#endif /* IPTS_MEI_H */ diff --git a/drivers/hid/ipts/receiver.c b/drivers/hid/ipts/receiver.c new file mode 100644 index 00000000000000..977724c728c3e5 --- /dev/null +++ b/drivers/hid/ipts/receiver.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "context.h" +#include "control.h" +#include "hid.h" +#include "receiver.h" +#include "resources.h" +#include "spec-device.h" +#include "thread.h" + +static void ipts_receiver_next_doorbell(struct ipts_context *ipts) +{ + u32 *doorbell = (u32 *)ipts->resources.doorbell.address; + *doorbell = *doorbell + 1; +} + +static u32 ipts_receiver_current_doorbell(struct ipts_context *ipts) +{ + u32 *doorbell = (u32 *)ipts->resources.doorbell.address; + return *doorbell; +} + +static void ipts_receiver_backoff(time64_t last, u32 n) +{ + /* + * If the last change was less than n seconds ago, + * sleep for a shorter period so that new data can be + * processed quickly. If there was no change for more than + * n seconds, sleep longer to avoid wasting CPU cycles. + */ + if (last + n > ktime_get_seconds()) + usleep_range(1 * USEC_PER_MSEC, 5 * USEC_PER_MSEC); + else + msleep(200); +} + +static int ipts_receiver_event_loop(struct ipts_thread *thread) +{ + int ret = 0; + u32 buffer = 0; + + struct ipts_context *ipts = NULL; + time64_t last = ktime_get_seconds(); + + if (!thread) + return -EFAULT; + + ipts = thread->data; + + if (!ipts) + return -EFAULT; + + dev_info(ipts->dev, "IPTS running in event mode\n"); + + while (!ipts_thread_should_stop(thread)) { + int i = 0; + + for (i = 0; i < IPTS_BUFFERS; i++) { + ret = ipts_control_wait_data(ipts, false); + if (ret == -EAGAIN) + break; + + if (ret) { + dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); + continue; + } + + buffer = ipts_receiver_current_doorbell(ipts) % IPTS_BUFFERS; + ipts_receiver_next_doorbell(ipts); + + ret = ipts_hid_input_data(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to process buffer: %d\n", ret); + + ret = ipts_control_refill_buffer(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to send feedback: %d\n", ret); + + ret = ipts_control_request_data(ipts); + if (ret) + dev_err(ipts->dev, "Failed to request data: %d\n", ret); + + last = ktime_get_seconds(); + } + + ipts_receiver_backoff(last, 5); + } + + ret = ipts_control_request_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to request flush: %d\n", ret); + return ret; + } + + ret = ipts_control_wait_data(ipts, true); + if (ret) { + dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + ret = ipts_control_wait_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + return 0; +} + +static int ipts_receiver_poll_loop(struct ipts_thread *thread) +{ + int ret = 0; + u32 buffer = 0; + + u32 doorbell = 0; + u32 lastdb = 0; + + struct ipts_context *ipts = NULL; + time64_t last = ktime_get_seconds(); + + if (!thread) + return -EFAULT; + + ipts = thread->data; + + if (!ipts) + return -EFAULT; + + dev_info(ipts->dev, "IPTS running in poll mode\n"); + + while (true) { + if (ipts_thread_should_stop(thread)) { + ret = ipts_control_request_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to request flush: %d\n", ret); + return ret; + } + } + + doorbell = ipts_receiver_current_doorbell(ipts); + + /* + * After filling up one of the data buffers, IPTS will increment + * the doorbell. The value of the doorbell stands for the *next* + * buffer that IPTS is going to fill. + */ + while (lastdb != doorbell) { + buffer = lastdb % IPTS_BUFFERS; + + ret = ipts_hid_input_data(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to process buffer: %d\n", ret); + + ret = ipts_control_refill_buffer(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to send feedback: %d\n", ret); + + last = ktime_get_seconds(); + lastdb++; + } + + if (ipts_thread_should_stop(thread)) + break; + + ipts_receiver_backoff(last, 5); + } + + ret = ipts_control_wait_data(ipts, true); + if (ret) { + dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + ret = ipts_control_wait_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + return 0; +} + +int ipts_receiver_start(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (ipts->mode == IPTS_MODE_EVENT) { + ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_event_loop, ipts, + "ipts_event"); + } else if (ipts->mode == IPTS_MODE_POLL) { + ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_poll_loop, ipts, + "ipts_poll"); + } else { + ret = -EINVAL; + } + + if (ret) { + dev_err(ipts->dev, "Failed to start receiver loop: %d\n", ret); + return ret; + } + + return 0; +} + +int ipts_receiver_stop(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + ret = ipts_thread_stop(&ipts->receiver_loop); + if (ret) { + dev_err(ipts->dev, "Failed to stop receiver loop: %d\n", ret); + return ret; + } + + return 0; +} diff --git a/drivers/hid/ipts/receiver.h b/drivers/hid/ipts/receiver.h new file mode 100644 index 00000000000000..3de7da62d40c14 --- /dev/null +++ b/drivers/hid/ipts/receiver.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_RECEIVER_H +#define IPTS_RECEIVER_H + +#include "context.h" + +int ipts_receiver_start(struct ipts_context *ipts); +int ipts_receiver_stop(struct ipts_context *ipts); + +#endif /* IPTS_RECEIVER_H */ diff --git a/drivers/hid/ipts/resources.c b/drivers/hid/ipts/resources.c new file mode 100644 index 00000000000000..cc14653b2a9f59 --- /dev/null +++ b/drivers/hid/ipts/resources.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include + +#include "desc.h" +#include "resources.h" +#include "spec-device.h" + +static int ipts_resources_alloc_buffer(struct ipts_buffer *buffer, struct device *dev, size_t size) +{ + if (!buffer) + return -EFAULT; + + if (buffer->address) + return 0; + + buffer->address = dma_alloc_coherent(dev, size, &buffer->dma_address, GFP_KERNEL); + + if (!buffer->address) + return -ENOMEM; + + buffer->size = size; + buffer->device = dev; + + return 0; +} + +static void ipts_resources_free_buffer(struct ipts_buffer *buffer) +{ + if (!buffer->address) + return; + + dma_free_coherent(buffer->device, buffer->size, buffer->address, buffer->dma_address); + + buffer->address = NULL; + buffer->size = 0; + + buffer->dma_address = 0; + buffer->device = NULL; +} + +int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs) +{ + int ret = 0; + + /* + * Some compilers (AOSP clang) complain about a redefined + * variable when this is declared inside of the for loop. + */ + int i = 0; + + if (!res) + return -EFAULT; + + for (i = 0; i < IPTS_BUFFERS; i++) { + ret = ipts_resources_alloc_buffer(&res->data[i], dev, ds); + if (ret) + goto err; + } + + for (i = 0; i < IPTS_BUFFERS; i++) { + ret = ipts_resources_alloc_buffer(&res->feedback[i], dev, fs); + if (ret) + goto err; + } + + ret = ipts_resources_alloc_buffer(&res->doorbell, dev, sizeof(u32)); + if (ret) + goto err; + + ret = ipts_resources_alloc_buffer(&res->workqueue, dev, sizeof(u32)); + if (ret) + goto err; + + ret = ipts_resources_alloc_buffer(&res->hid2me, dev, fs); + if (ret) + goto err; + + ret = ipts_resources_alloc_buffer(&res->descriptor, dev, ds + 8); + if (ret) + goto err; + + if (!res->report.address) { + res->report.size = IPTS_HID_REPORT_DATA_SIZE; + res->report.address = kzalloc(res->report.size, GFP_KERNEL); + + if (!res->report.address) { + ret = -ENOMEM; + goto err; + } + } + + return 0; + +err: + + ipts_resources_free(res); + return ret; +} + +int ipts_resources_free(struct ipts_resources *res) +{ + int i = 0; + + if (!res) + return -EFAULT; + + for (i = 0; i < IPTS_BUFFERS; i++) + ipts_resources_free_buffer(&res->data[i]); + + for (i = 0; i < IPTS_BUFFERS; i++) + ipts_resources_free_buffer(&res->feedback[i]); + + ipts_resources_free_buffer(&res->doorbell); + ipts_resources_free_buffer(&res->workqueue); + ipts_resources_free_buffer(&res->hid2me); + ipts_resources_free_buffer(&res->descriptor); + + kfree(res->report.address); + res->report.address = NULL; + res->report.size = 0; + + return 0; +} diff --git a/drivers/hid/ipts/resources.h b/drivers/hid/ipts/resources.h new file mode 100644 index 00000000000000..2068e13285f0ef --- /dev/null +++ b/drivers/hid/ipts/resources.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_RESOURCES_H +#define IPTS_RESOURCES_H + +#include +#include + +#include "spec-device.h" + +struct ipts_buffer { + u8 *address; + size_t size; + + dma_addr_t dma_address; + struct device *device; +}; + +struct ipts_resources { + struct ipts_buffer data[IPTS_BUFFERS]; + struct ipts_buffer feedback[IPTS_BUFFERS]; + + struct ipts_buffer doorbell; + struct ipts_buffer workqueue; + struct ipts_buffer hid2me; + + struct ipts_buffer descriptor; + + // Buffer for synthesizing HID reports + struct ipts_buffer report; +}; + +int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs); +int ipts_resources_free(struct ipts_resources *res); + +#endif /* IPTS_RESOURCES_H */ diff --git a/drivers/hid/ipts/spec-data.h b/drivers/hid/ipts/spec-data.h new file mode 100644 index 00000000000000..e8dd98895a7eee --- /dev/null +++ b/drivers/hid/ipts/spec-data.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_SPEC_DATA_H +#define IPTS_SPEC_DATA_H + +#include +#include + +/** + * enum ipts_feedback_cmd_type - Commands that can be executed on the sensor through feedback. + */ +enum ipts_feedback_cmd_type { + IPTS_FEEDBACK_CMD_TYPE_NONE = 0, + IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET = 1, + IPTS_FEEDBACK_CMD_TYPE_GOTO_ARMED = 2, + IPTS_FEEDBACK_CMD_TYPE_GOTO_SENSING = 3, + IPTS_FEEDBACK_CMD_TYPE_GOTO_SLEEP = 4, + IPTS_FEEDBACK_CMD_TYPE_GOTO_DOZE = 5, + IPTS_FEEDBACK_CMD_TYPE_HARD_RESET = 6, +}; + +/** + * enum ipts_feedback_data_type - Defines what data a feedback buffer contains. + * @IPTS_FEEDBACK_DATA_TYPE_VENDOR: The buffer contains vendor specific feedback. + * @IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES: The buffer contains a HID set features report. + * @IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES: The buffer contains a HID get features report. + * @IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT: The buffer contains a HID output report. + * @IPTS_FEEDBACK_DATA_TYPE_STORE_DATA: The buffer contains calibration data for the sensor. + */ +enum ipts_feedback_data_type { + IPTS_FEEDBACK_DATA_TYPE_VENDOR = 0, + IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES = 1, + IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES = 2, + IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT = 3, + IPTS_FEEDBACK_DATA_TYPE_STORE_DATA = 4, +}; + +/** + * struct ipts_feedback_header - Header that is prefixed to the data in a feedback buffer. + * @cmd_type: A command that should be executed on the sensor. + * @size: The size of the payload to be written. + * @buffer: The ID of the buffer that contains this feedback data. + * @protocol: The protocol version of the EDS. + * @data_type: The type of data that the buffer contains. + * @spi_offset: The offset at which to write the payload data to the sensor. + * @payload: Payload for the feedback command, or 0 if no payload is sent. + */ +struct ipts_feedback_header { + enum ipts_feedback_cmd_type cmd_type; + u32 size; + u32 buffer; + u32 protocol; + enum ipts_feedback_data_type data_type; + u32 spi_offset; + u8 reserved[40]; + u8 payload[]; +} __packed; + +static_assert(sizeof(struct ipts_feedback_header) == 64); + +/** + * enum ipts_data_type - Defines what type of data a buffer contains. + * @IPTS_DATA_TYPE_FRAME: Raw data frame. + * @IPTS_DATA_TYPE_ERROR: Error data. + * @IPTS_DATA_TYPE_VENDOR: Vendor specific data. + * @IPTS_DATA_TYPE_HID: A HID report. + * @IPTS_DATA_TYPE_GET_FEATURES: The response to a GET_FEATURES HID2ME command. + */ +enum ipts_data_type { + IPTS_DATA_TYPE_FRAME = 0x00, + IPTS_DATA_TYPE_ERROR = 0x01, + IPTS_DATA_TYPE_VENDOR = 0x02, + IPTS_DATA_TYPE_HID = 0x03, + IPTS_DATA_TYPE_GET_FEATURES = 0x04, + IPTS_DATA_TYPE_DESCRIPTOR = 0x05, +}; + +/** + * struct ipts_data_header - Header that is prefixed to the data in a data buffer. + * @type: What data the buffer contains. + * @size: How much data the buffer contains. + * @buffer: Which buffer the data is in. + */ +struct ipts_data_header { + enum ipts_data_type type; + u32 size; + u32 buffer; + u8 reserved[52]; + u8 data[]; +} __packed; + +static_assert(sizeof(struct ipts_data_header) == 64); + +#endif /* IPTS_SPEC_DATA_H */ diff --git a/drivers/hid/ipts/spec-device.h b/drivers/hid/ipts/spec-device.h new file mode 100644 index 00000000000000..41845f9d902579 --- /dev/null +++ b/drivers/hid/ipts/spec-device.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_SPEC_DEVICE_H +#define IPTS_SPEC_DEVICE_H + +#include +#include + +/* + * The amount of buffers that IPTS can use for data transfer. + */ +#define IPTS_BUFFERS 16 + +/* + * The buffer ID that is used for HID2ME feedback + */ +#define IPTS_HID2ME_BUFFER IPTS_BUFFERS + +/** + * enum ipts_command - Commands that can be sent to the IPTS hardware. + * @IPTS_CMD_GET_DEVICE_INFO: Retrieves vendor information from the device. + * @IPTS_CMD_SET_MODE: Changes the mode that the device will operate in. + * @IPTS_CMD_SET_MEM_WINDOW: Configures memory buffers for passing data between device and driver. + * @IPTS_CMD_QUIESCE_IO: Stops the data flow from the device to the driver. + * @IPTS_CMD_READY_FOR_DATA: Informs the device that the driver is ready to receive data. + * @IPTS_CMD_FEEDBACK: Informs the device that a buffer was processed and can be refilled. + * @IPTS_CMD_CLEAR_MEM_WINDOW: Stops the data flow and clears the buffer addresses on the device. + * @IPTS_CMD_RESET_SENSOR: Resets the sensor to its default state. + * @IPTS_CMD_GET_DESCRIPTOR: Retrieves the HID descriptor of the device. + */ +enum ipts_command_code { + IPTS_CMD_GET_DEVICE_INFO = 0x01, + IPTS_CMD_SET_MODE = 0x02, + IPTS_CMD_SET_MEM_WINDOW = 0x03, + IPTS_CMD_QUIESCE_IO = 0x04, + IPTS_CMD_READY_FOR_DATA = 0x05, + IPTS_CMD_FEEDBACK = 0x06, + IPTS_CMD_CLEAR_MEM_WINDOW = 0x07, + IPTS_CMD_RESET_SENSOR = 0x0B, + IPTS_CMD_GET_DESCRIPTOR = 0x0F, +}; + +/** + * enum ipts_status - Possible status codes returned by the IPTS device. + * @IPTS_STATUS_SUCCESS: Operation completed successfully. + * @IPTS_STATUS_INVALID_PARAMS: Command contained an invalid payload. + * @IPTS_STATUS_ACCESS_DENIED: ME could not validate a buffer address. + * @IPTS_STATUS_CMD_SIZE_ERROR: Command contains an invalid payload. + * @IPTS_STATUS_NOT_READY: Buffer addresses have not been set. + * @IPTS_STATUS_REQUEST_OUTSTANDING: There is an outstanding command of the same type. + * @IPTS_STATUS_NO_SENSOR_FOUND: No sensor could be found. + * @IPTS_STATUS_OUT_OF_MEMORY: Not enough free memory for requested operation. + * @IPTS_STATUS_INTERNAL_ERROR: An unexpected error occurred. + * @IPTS_STATUS_SENSOR_DISABLED: The sensor has been disabled and must be reinitialized. + * @IPTS_STATUS_COMPAT_CHECK_FAIL: Compatibility revision check between sensor and ME failed. + * The host can ignore this error and attempt to continue. + * @IPTS_STATUS_SENSOR_EXPECTED_RESET: The sensor went through a reset initiated by the driver. + * @IPTS_STATUS_SENSOR_UNEXPECTED_RESET: The sensor went through an unexpected reset. + * @IPTS_STATUS_RESET_FAILED: Requested sensor reset failed to complete. + * @IPTS_STATUS_TIMEOUT: The operation timed out. + * @IPTS_STATUS_TEST_MODE_FAIL: Test mode pattern did not match expected values. + * @IPTS_STATUS_SENSOR_FAIL_FATAL: The sensor reported an error during reset sequence. + * Further progress is not possible. + * @IPTS_STATUS_SENSOR_FAIL_NONFATAL: The sensor reported an error during reset sequence. + * The driver can attempt to continue. + * @IPTS_STATUS_INVALID_DEVICE_CAPS: The device reported invalid capabilities. + * @IPTS_STATUS_QUIESCE_IO_IN_PROGRESS: Command cannot be completed until Quiesce IO is done. + */ +enum ipts_status { + IPTS_STATUS_SUCCESS = 0x00, + IPTS_STATUS_INVALID_PARAMS = 0x01, + IPTS_STATUS_ACCESS_DENIED = 0x02, + IPTS_STATUS_CMD_SIZE_ERROR = 0x03, + IPTS_STATUS_NOT_READY = 0x04, + IPTS_STATUS_REQUEST_OUTSTANDING = 0x05, + IPTS_STATUS_NO_SENSOR_FOUND = 0x06, + IPTS_STATUS_OUT_OF_MEMORY = 0x07, + IPTS_STATUS_INTERNAL_ERROR = 0x08, + IPTS_STATUS_SENSOR_DISABLED = 0x09, + IPTS_STATUS_COMPAT_CHECK_FAIL = 0x0A, + IPTS_STATUS_SENSOR_EXPECTED_RESET = 0x0B, + IPTS_STATUS_SENSOR_UNEXPECTED_RESET = 0x0C, + IPTS_STATUS_RESET_FAILED = 0x0D, + IPTS_STATUS_TIMEOUT = 0x0E, + IPTS_STATUS_TEST_MODE_FAIL = 0x0F, + IPTS_STATUS_SENSOR_FAIL_FATAL = 0x10, + IPTS_STATUS_SENSOR_FAIL_NONFATAL = 0x11, + IPTS_STATUS_INVALID_DEVICE_CAPS = 0x12, + IPTS_STATUS_QUIESCE_IO_IN_PROGRESS = 0x13, +}; + +/** + * struct ipts_command - Message that is sent to the device for calling a command. + * @cmd: The command that will be called. + * @payload: Payload containing parameters for the called command. + */ +struct ipts_command { + enum ipts_command_code cmd; + u8 payload[320]; +} __packed; + +static_assert(sizeof(struct ipts_command) == 324); + +/** + * enum ipts_mode - Configures what data the device produces and how its sent. + * @IPTS_MODE_EVENT: The device will send an event once a buffer was filled. + * Older devices will return singletouch data in this mode. + * @IPTS_MODE_POLL: The device will notify the driver by incrementing the doorbell value. + * Older devices will return multitouch data in this mode. + */ +enum ipts_mode { + IPTS_MODE_EVENT = 0x00, + IPTS_MODE_POLL = 0x01, +}; + +/** + * struct ipts_set_mode - Payload for the SET_MODE command. + * @mode: Changes the mode that IPTS will operate in. + */ +struct ipts_set_mode { + enum ipts_mode mode; + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_set_mode) == 16); + +#define IPTS_WORKQUEUE_SIZE 8192 +#define IPTS_WORKQUEUE_ITEM_SIZE 16 + +/** + * struct ipts_mem_window - Payload for the SET_MEM_WINDOW command. + * @data_addr_lower: Lower 32 bits of the data buffer addresses. + * @data_addr_upper: Upper 32 bits of the data buffer addresses. + * @workqueue_addr_lower: Lower 32 bits of the workqueue buffer address. + * @workqueue_addr_upper: Upper 32 bits of the workqueue buffer address. + * @doorbell_addr_lower: Lower 32 bits of the doorbell buffer address. + * @doorbell_addr_upper: Upper 32 bits of the doorbell buffer address. + * @feedbackaddr_lower: Lower 32 bits of the feedback buffer addresses. + * @feedbackaddr_upper: Upper 32 bits of the feedback buffer addresses. + * @hid2me_addr_lower: Lower 32 bits of the hid2me buffer address. + * @hid2me_addr_upper: Upper 32 bits of the hid2me buffer address. + * @hid2me_size: Size of the hid2me feedback buffer. + * @workqueue_item_size: Magic value. Must be 16. + * @workqueue_size: Magic value. Must be 8192. + * + * The workqueue related items in this struct are required for using + * GuC submission with binary processing firmware. Since this driver does + * not use GuC submission and instead exports raw data to userspace, these + * items are not actually used, but they need to be allocated and passed + * to the device, otherwise initialization will fail. + */ +struct ipts_mem_window { + u32 data_addr_lower[IPTS_BUFFERS]; + u32 data_addr_upper[IPTS_BUFFERS]; + u32 workqueue_addr_lower; + u32 workqueue_addr_upper; + u32 doorbell_addr_lower; + u32 doorbell_addr_upper; + u32 feedback_addr_lower[IPTS_BUFFERS]; + u32 feedback_addr_upper[IPTS_BUFFERS]; + u32 hid2me_addr_lower; + u32 hid2me_addr_upper; + u32 hid2me_size; + u8 reserved1; + u8 workqueue_item_size; + u16 workqueue_size; + u8 reserved[32]; +} __packed; + +static_assert(sizeof(struct ipts_mem_window) == 320); + +/** + * struct ipts_quiesce_io - Payload for the QUIESCE_IO command. + */ +struct ipts_quiesce_io { + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_quiesce_io) == 12); + +/** + * struct ipts_feedback - Payload for the FEEDBACK command. + * @buffer: The buffer that the device should refill. + */ +struct ipts_feedback { + u32 buffer; + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_feedback) == 16); + +/** + * enum ipts_reset_type - Possible ways of resetting the device. + * @IPTS_RESET_TYPE_HARD: Perform hardware reset using GPIO pin. + * @IPTS_RESET_TYPE_SOFT: Perform software reset using SPI command. + */ +enum ipts_reset_type { + IPTS_RESET_TYPE_HARD = 0x00, + IPTS_RESET_TYPE_SOFT = 0x01, +}; + +/** + * struct ipts_reset - Payload for the RESET_SENSOR command. + * @type: How the device should get reset. + */ +struct ipts_reset_sensor { + enum ipts_reset_type type; + u8 reserved[4]; +} __packed; + +static_assert(sizeof(struct ipts_reset_sensor) == 8); + +/** + * struct ipts_get_descriptor - Payload for the GET_DESCRIPTOR command. + * @addr_lower: The lower 32 bits of the descriptor buffer address. + * @addr_upper: The upper 32 bits of the descriptor buffer address. + * @magic: A magic value. Must be 8. + */ +struct ipts_get_descriptor { + u32 addr_lower; + u32 addr_upper; + u32 magic; + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_get_descriptor) == 24); + +/* + * The type of a response is indicated by a + * command code, with the most significant bit flipped to 1. + */ +#define IPTS_RSP_BIT BIT(31) + +/** + * struct ipts_response - Data returned from the device in response to a command. + * @cmd: The command that this response answers (IPTS_RSP_BIT will be 1). + * @status: The return code of the command. + * @payload: The data that was produced by the command. + */ +struct ipts_response { + enum ipts_command_code cmd; + enum ipts_status status; + u8 payload[80]; +} __packed; + +static_assert(sizeof(struct ipts_response) == 88); + +/** + * struct ipts_device_info - Vendor information of the IPTS device. + * @vendor: Vendor ID of this device. + * @product: Product ID of this device. + * @hw_version: Hardware revision of this device. + * @fw_version: Firmware revision of this device. + * @data_size: Requested size for a data buffer. + * @feedback_size: Requested size for a feedback buffer. + * @mode: Mode that the device currently operates in. + * @max_contacts: Maximum amount of concurrent touches the sensor can process. + * @sensor_min_eds: The minimum EDS version supported by the sensor. + * @sensor_max_eds: The maximum EDS version supported by the sensor. + * @me_min_eds: The minimum EDS version supported by the ME for communicating with the sensor. + * @me_max_eds: The maximum EDS version supported by the ME for communicating with the sensor. + * @intf_eds: The EDS version implemented by the interface between ME and host. + */ +struct ipts_device_info { + u16 vendor; + u16 product; + u32 hw_version; + u32 fw_version; + u32 data_size; + u32 feedback_size; + enum ipts_mode mode; + u8 max_contacts; + u8 reserved1[3]; + u8 sensor_min_eds; + u8 sensor_maj_eds; + u8 me_min_eds; + u8 me_maj_eds; + u8 intf_eds; + u8 reserved2[11]; +} __packed; + +static_assert(sizeof(struct ipts_device_info) == 44); + +#endif /* IPTS_SPEC_DEVICE_H */ diff --git a/drivers/hid/ipts/spec-hid.h b/drivers/hid/ipts/spec-hid.h new file mode 100644 index 00000000000000..5a58d4a0a610f4 --- /dev/null +++ b/drivers/hid/ipts/spec-hid.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_SPEC_HID_H +#define IPTS_SPEC_HID_H + +#include +#include + +/* + * Made-up type for passing raw IPTS data in a HID report. + */ +#define IPTS_HID_FRAME_TYPE_RAW 0xEE + +/** + * struct ipts_hid_frame - Header that is prefixed to raw IPTS data wrapped in a HID report. + * @size: Size of the data inside the report, including this header. + * @type: What type of data does this report contain. + */ +struct ipts_hid_header { + u32 size; + u8 reserved1; + u8 type; + u8 reserved2; + u8 data[]; +} __packed; + +static_assert(sizeof(struct ipts_hid_header) == 7); + +#endif /* IPTS_SPEC_HID_H */ diff --git a/drivers/hid/ipts/thread.c b/drivers/hid/ipts/thread.c new file mode 100644 index 00000000000000..355e92bea26f81 --- /dev/null +++ b/drivers/hid/ipts/thread.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include + +#include "thread.h" + +bool ipts_thread_should_stop(struct ipts_thread *thread) +{ + if (!thread) + return false; + + return READ_ONCE(thread->should_stop); +} + +static int ipts_thread_runner(void *data) +{ + int ret = 0; + struct ipts_thread *thread = data; + + if (!thread) + return -EFAULT; + + if (!thread->threadfn) + return -EFAULT; + + ret = thread->threadfn(thread); + complete_all(&thread->done); + + return ret; +} + +int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread), + void *data, const char *name) +{ + if (!thread) + return -EFAULT; + + if (!threadfn) + return -EFAULT; + + init_completion(&thread->done); + + thread->data = data; + thread->should_stop = false; + thread->threadfn = threadfn; + + thread->thread = kthread_run(ipts_thread_runner, thread, name); + return PTR_ERR_OR_ZERO(thread->thread); +} + +int ipts_thread_stop(struct ipts_thread *thread) +{ + int ret = 0; + + if (!thread) + return -EFAULT; + + if (!thread->thread) + return 0; + + WRITE_ONCE(thread->should_stop, true); + + /* + * Make sure that the write has gone through before waiting. + */ + wmb(); + + wait_for_completion(&thread->done); + ret = kthread_stop(thread->thread); + + thread->thread = NULL; + thread->data = NULL; + thread->threadfn = NULL; + + return ret; +} diff --git a/drivers/hid/ipts/thread.h b/drivers/hid/ipts/thread.h new file mode 100644 index 00000000000000..1f966b8b32c45b --- /dev/null +++ b/drivers/hid/ipts/thread.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_THREAD_H +#define IPTS_THREAD_H + +#include +#include +#include + +/* + * This wrapper over kthread is necessary, because calling kthread_stop makes it impossible + * to issue MEI commands from that thread while it shuts itself down. By using a custom + * boolean variable and a completion object, we can call kthread_stop only when the thread + * already finished all of its work and has returned. + */ +struct ipts_thread { + struct task_struct *thread; + + bool should_stop; + struct completion done; + + void *data; + int (*threadfn)(struct ipts_thread *thread); +}; + +/** + * ipts_thread_should_stop() - Returns true if the thread is asked to terminate. + * @thread: The current thread. + * + * Returns: true if the thread should stop, false if not. + */ +bool ipts_thread_should_stop(struct ipts_thread *thread); + +/** + * ipts_thread_start() - Starts an IPTS thread. + * @thread: The thread to initialize and start. + * @threadfn: The function to execute. + * @data: An argument that will be passed to threadfn. + * @name: The name of the new thread. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread), + void *data, const char name[]); + +/** + * ipts_thread_stop() - Asks the thread to terminate and waits until it has finished. + * @thread: The thread that should stop. + * + * Returns: The return value of the thread function. + */ +int ipts_thread_stop(struct ipts_thread *thread); + +#endif /* IPTS_THREAD_H */ From 47970bdd3003e165e20aad9df4e7ea6a4156dedd Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Sun, 11 Dec 2022 12:03:38 +0100 Subject: [PATCH 406/521] SURFACE: iommu: intel: Disable source id verification for ITHC Signed-off-by: Dorian Stoll Patchset: ithc Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/iommu/intel/irq_remapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/iommu/intel/irq_remapping.c b/drivers/iommu/intel/irq_remapping.c index 25c26f706984a4..772bb0d8e5888b 100644 --- a/drivers/iommu/intel/irq_remapping.c +++ b/drivers/iommu/intel/irq_remapping.c @@ -381,6 +381,22 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev) data.busmatch_count = 0; pci_for_each_dma_alias(dev, set_msi_sid_cb, &data); + /* + * The Intel Touch Host Controller is at 00:10.6, but for some reason + * the MSI interrupts have request id 01:05.0. + * Disable id verification to work around this. + * FIXME Find proper fix or turn this into a quirk. + */ + if (dev->vendor == PCI_VENDOR_ID_INTEL && (dev->class >> 8) == PCI_CLASS_INPUT_PEN) { + switch(dev->device) { + case 0x98d0: case 0x98d1: // LKF + case 0xa0d0: case 0xa0d1: // TGL LP + case 0x43d0: case 0x43d1: // TGL H + set_irte_sid(irte, SVT_NO_VERIFY, SQ_ALL_16, 0); + return 0; + } + } + /* * DMA alias provides us with a PCI device and alias. The only case * where the it will return an alias on a different bus than the From 6c0f8c669e5f25c64f8684402d9a4a45d31cedb7 Mon Sep 17 00:00:00 2001 From: quo Date: Sun, 11 Dec 2022 12:10:54 +0100 Subject: [PATCH 407/521] SURFACE: hid: Add support for Intel Touch Host Controller Based on quo/ithc-linux@34539af4726d. Signed-off-by: Maximilian Stoll Patchset: ithc Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 1 + drivers/hid/ithc/Kbuild | 6 + drivers/hid/ithc/Kconfig | 12 + drivers/hid/ithc/ithc-debug.c | 149 ++++++++ drivers/hid/ithc/ithc-debug.h | 7 + drivers/hid/ithc/ithc-dma.c | 312 ++++++++++++++++ drivers/hid/ithc/ithc-dma.h | 47 +++ drivers/hid/ithc/ithc-hid.c | 207 +++++++++++ drivers/hid/ithc/ithc-hid.h | 32 ++ drivers/hid/ithc/ithc-legacy.c | 254 +++++++++++++ drivers/hid/ithc/ithc-legacy.h | 8 + drivers/hid/ithc/ithc-main.c | 438 ++++++++++++++++++++++ drivers/hid/ithc/ithc-quickspi.c | 607 +++++++++++++++++++++++++++++++ drivers/hid/ithc/ithc-quickspi.h | 39 ++ drivers/hid/ithc/ithc-regs.c | 154 ++++++++ drivers/hid/ithc/ithc-regs.h | 211 +++++++++++ drivers/hid/ithc/ithc.h | 89 +++++ 18 files changed, 2575 insertions(+) create mode 100644 drivers/hid/ithc/Kbuild create mode 100644 drivers/hid/ithc/Kconfig create mode 100644 drivers/hid/ithc/ithc-debug.c create mode 100644 drivers/hid/ithc/ithc-debug.h create mode 100644 drivers/hid/ithc/ithc-dma.c create mode 100644 drivers/hid/ithc/ithc-dma.h create mode 100644 drivers/hid/ithc/ithc-hid.c create mode 100644 drivers/hid/ithc/ithc-hid.h create mode 100644 drivers/hid/ithc/ithc-legacy.c create mode 100644 drivers/hid/ithc/ithc-legacy.h create mode 100644 drivers/hid/ithc/ithc-main.c create mode 100644 drivers/hid/ithc/ithc-quickspi.c create mode 100644 drivers/hid/ithc/ithc-quickspi.h create mode 100644 drivers/hid/ithc/ithc-regs.c create mode 100644 drivers/hid/ithc/ithc-regs.h create mode 100644 drivers/hid/ithc/ithc.h diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cecb264a229f99..2886bde63605c3 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1468,6 +1468,8 @@ source "drivers/hid/intel-thc-hid/Kconfig" source "drivers/hid/ipts/Kconfig" +source "drivers/hid/ithc/Kconfig" + endif # HID # USB support may be used with HID disabled diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index b46dec8ad0bfbb..fdc4e3d459d15a 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -182,3 +182,4 @@ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ obj-$(CONFIG_HID_IPTS) += ipts/ +obj-$(CONFIG_HID_ITHC) += ithc/ diff --git a/drivers/hid/ithc/Kbuild b/drivers/hid/ithc/Kbuild new file mode 100644 index 00000000000000..4937ba1312973a --- /dev/null +++ b/drivers/hid/ithc/Kbuild @@ -0,0 +1,6 @@ +obj-$(CONFIG_HID_ITHC) := ithc.o + +ithc-objs := ithc-main.o ithc-regs.o ithc-dma.o ithc-hid.o ithc-legacy.o ithc-quickspi.o ithc-debug.o + +ccflags-y := -std=gnu11 -Wno-declaration-after-statement + diff --git a/drivers/hid/ithc/Kconfig b/drivers/hid/ithc/Kconfig new file mode 100644 index 00000000000000..ede71302360964 --- /dev/null +++ b/drivers/hid/ithc/Kconfig @@ -0,0 +1,12 @@ +config HID_ITHC + tristate "Intel Touch Host Controller" + depends on PCI + depends on HID + help + Say Y here if your system has a touchscreen using Intels + Touch Host Controller (ITHC / IPTS) technology. + + If unsure say N. + + To compile this driver as a module, choose M here: the + module will be called ithc. diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c new file mode 100644 index 00000000000000..2d8c6afe99663a --- /dev/null +++ b/drivers/hid/ithc/ithc-debug.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +void ithc_log_regs(struct ithc *ithc) +{ + if (!ithc->prev_regs) + return; + u32 __iomem *cur = (__iomem void *)ithc->regs; + u32 *prev = (void *)ithc->prev_regs; + for (int i = 1024; i < sizeof(*ithc->regs) / 4; i++) { + u32 x = readl(cur + i); + if (x != prev[i]) { + pci_info(ithc->pci, "reg %04x: %08x -> %08x\n", i * 4, prev[i], x); + prev[i] = x; + } + } +} + +static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len, + loff_t *offset) +{ + // Debug commands consist of a single letter followed by a list of numbers (decimal or + // hexadecimal, space-separated). + struct ithc *ithc = file_inode(f)->i_private; + char cmd[256]; + if (!ithc || !ithc->pci) + return -ENODEV; + if (!len) + return -EINVAL; + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + cmd[len] = 0; + if (cmd[len-1] == '\n') + cmd[len-1] = 0; + pci_info(ithc->pci, "debug command: %s\n", cmd); + + // Parse the list of arguments into a u32 array. + u32 n = 0; + const char *s = cmd + 1; + u32 a[32]; + while (*s && *s != '\n') { + if (n >= ARRAY_SIZE(a)) + return -EINVAL; + if (*s++ != ' ') + return -EINVAL; + char *e; + a[n++] = simple_strtoul(s, &e, 0); + if (e == s) + return -EINVAL; + s = e; + } + ithc_log_regs(ithc); + + // Execute the command. + switch (cmd[0]) { + case 'x': // reset + ithc_reset(ithc); + break; + case 'w': // write register: offset mask value + if (n != 3 || (a[0] & 3)) + return -EINVAL; + pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n", + a[0], a[2], a[1]); + bitsl(((__iomem u32 *)ithc->regs) + a[0] / 4, a[1], a[2]); + break; + case 'r': // read register: offset + if (n != 1 || (a[0] & 3)) + return -EINVAL; + pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0], + readl(((__iomem u32 *)ithc->regs) + a[0] / 4)); + break; + case 's': // spi command: cmd offset len data... + // read config: s 4 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // set touch cfg: s 6 12 4 XX + if (n < 3 || a[2] > (n - 3) * 4) + return -EINVAL; + pci_info(ithc->pci, "debug spi command %u with %u bytes of data\n", a[0], a[2]); + if (!CHECK(ithc_spi_command, ithc, a[0], a[1], a[2], a + 3)) + for (u32 i = 0; i < (a[2] + 3) / 4; i++) + pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]); + break; + case 'd': // dma command: cmd len data... + // get report descriptor: d 7 8 0 0 + // enable multitouch: d 3 2 0x0105 + if (n < 1) + return -EINVAL; + pci_info(ithc->pci, "debug dma command with %u bytes of data\n", n * 4); + struct ithc_data data = { .type = ITHC_DATA_RAW, .size = n * 4, .data = a }; + if (ithc_dma_tx(ithc, &data)) + pci_err(ithc->pci, "dma tx failed\n"); + break; + default: + return -EINVAL; + } + ithc_log_regs(ithc); + return len; +} + +static struct dentry *dbg_dir; + +void __init ithc_debug_init_module(void) +{ + struct dentry *d = debugfs_create_dir(DEVNAME, NULL); + if (IS_ERR(d)) + pr_warn("failed to create debugfs dir (%li)\n", PTR_ERR(d)); + else + dbg_dir = d; +} + +void __exit ithc_debug_exit_module(void) +{ + debugfs_remove_recursive(dbg_dir); + dbg_dir = NULL; +} + +static const struct file_operations ithc_debugfops_cmd = { + .owner = THIS_MODULE, + .write = ithc_debugfs_cmd_write, +}; + +static void ithc_debugfs_devres_release(struct device *dev, void *res) +{ + struct dentry **dbgm = res; + debugfs_remove_recursive(*dbgm); +} + +int ithc_debug_init_device(struct ithc *ithc) +{ + if (!dbg_dir) + return -ENOENT; + struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof(*dbgm), GFP_KERNEL); + if (!dbgm) + return -ENOMEM; + devres_add(&ithc->pci->dev, dbgm); + struct dentry *dbg = debugfs_create_dir(pci_name(ithc->pci), dbg_dir); + if (IS_ERR(dbg)) + return PTR_ERR(dbg); + *dbgm = dbg; + + struct dentry *cmd = debugfs_create_file("cmd", 0220, dbg, ithc, &ithc_debugfops_cmd); + if (IS_ERR(cmd)) + return PTR_ERR(cmd); + + return 0; +} + diff --git a/drivers/hid/ithc/ithc-debug.h b/drivers/hid/ithc/ithc-debug.h new file mode 100644 index 00000000000000..38c53d916bdb5e --- /dev/null +++ b/drivers/hid/ithc/ithc-debug.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +void ithc_debug_init_module(void); +void ithc_debug_exit_module(void); +int ithc_debug_init_device(struct ithc *ithc); +void ithc_log_regs(struct ithc *ithc); + diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c new file mode 100644 index 00000000000000..bf4eab33062b0e --- /dev/null +++ b/drivers/hid/ithc/ithc-dma.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +// The THC uses tables of PRDs (physical region descriptors) to describe the TX and RX data buffers. +// Each PRD contains the DMA address and size of a block of DMA memory, and some status flags. +// This allows each data buffer to consist of multiple non-contiguous blocks of memory. + +static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p, + unsigned int num_buffers, unsigned int num_pages, enum dma_data_direction dir) +{ + p->num_pages = num_pages; + p->dir = dir; + // We allocate enough space to have one PRD per data buffer page, however if the data + // buffer pages happen to be contiguous, we can describe the buffer using fewer PRDs, so + // some will remain unused (which is fine). + p->size = round_up(num_buffers * num_pages * sizeof(struct ithc_phys_region_desc), PAGE_SIZE); + p->addr = dmam_alloc_coherent(&ithc->pci->dev, p->size, &p->dma_addr, GFP_KERNEL); + if (!p->addr) + return -ENOMEM; + if (p->dma_addr & (PAGE_SIZE - 1)) + return -EFAULT; + return 0; +} + +// Devres managed sg_table wrapper. +struct ithc_sg_table { + void *addr; + struct sg_table sgt; + enum dma_data_direction dir; +}; +static void ithc_dma_sgtable_free(struct sg_table *sgt) +{ + struct scatterlist *sg; + int i; + for_each_sgtable_sg(sgt, sg, i) { + struct page *p = sg_page(sg); + if (p) + __free_page(p); + } + sg_free_table(sgt); +} +static void ithc_dma_data_devres_release(struct device *dev, void *res) +{ + struct ithc_sg_table *sgt = res; + if (sgt->addr) + vunmap(sgt->addr); + dma_unmap_sgtable(dev, &sgt->sgt, sgt->dir, 0); + ithc_dma_sgtable_free(&sgt->sgt); +} + +static int ithc_dma_data_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, + struct ithc_dma_data_buffer *b) +{ + // We don't use dma_alloc_coherent() for data buffers, because they don't have to be + // coherent (they are unidirectional) or contiguous (we can use one PRD per page). + // We could use dma_alloc_noncontiguous(), however this still always allocates a single + // DMA mapped segment, which is more restrictive than what we need. + // Instead we use an sg_table of individually allocated pages. + struct page *pages[16]; + if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages)) + return -EINVAL; + b->active_idx = -1; + struct ithc_sg_table *sgt = devres_alloc( + ithc_dma_data_devres_release, sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + sgt->dir = prds->dir; + + if (!sg_alloc_table(&sgt->sgt, prds->num_pages, GFP_KERNEL)) { + struct scatterlist *sg; + int i; + bool ok = true; + for_each_sgtable_sg(&sgt->sgt, sg, i) { + // NOTE: don't need __GFP_DMA for PCI DMA + struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!p) { + ok = false; + break; + } + sg_set_page(sg, p, PAGE_SIZE, 0); + } + if (ok && !dma_map_sgtable(&ithc->pci->dev, &sgt->sgt, prds->dir, 0)) { + devres_add(&ithc->pci->dev, sgt); + b->sgt = &sgt->sgt; + b->addr = sgt->addr = vmap(pages, prds->num_pages, 0, PAGE_KERNEL); + if (!b->addr) + return -ENOMEM; + return 0; + } + ithc_dma_sgtable_free(&sgt->sgt); + } + devres_free(sgt); + return -ENOMEM; +} + +static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, + struct ithc_dma_data_buffer *b, unsigned int idx) +{ + // Give a buffer to the THC. + struct ithc_phys_region_desc *prd = prds->addr; + prd += idx * prds->num_pages; + if (b->active_idx >= 0) { + pci_err(ithc->pci, "buffer already active\n"); + return -EINVAL; + } + b->active_idx = idx; + if (prds->dir == DMA_TO_DEVICE) { + // TX buffer: Caller should have already filled the data buffer, so just fill + // the PRD and flush. + // (TODO: Support multi-page TX buffers. So far no device seems to use or need + // these though.) + if (b->data_size > PAGE_SIZE) + return -EINVAL; + prd->addr = sg_dma_address(b->sgt->sgl) >> 10; + prd->size = b->data_size | PRD_FLAG_END; + flush_kernel_vmap_range(b->addr, b->data_size); + } else if (prds->dir == DMA_FROM_DEVICE) { + // RX buffer: Reset PRDs. + struct scatterlist *sg; + int i; + for_each_sgtable_dma_sg(b->sgt, sg, i) { + prd->addr = sg_dma_address(sg) >> 10; + prd->size = sg_dma_len(sg); + prd++; + } + prd[-1].size |= PRD_FLAG_END; + } + dma_wmb(); // for the prds + dma_sync_sgtable_for_device(&ithc->pci->dev, b->sgt, prds->dir); + return 0; +} + +static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, + struct ithc_dma_data_buffer *b, unsigned int idx) +{ + // Take a buffer from the THC. + struct ithc_phys_region_desc *prd = prds->addr; + prd += idx * prds->num_pages; + // This is purely a sanity check. We don't strictly need the idx parameter for this + // function, because it should always be the same as active_idx, unless we have a bug. + if (b->active_idx != idx) { + pci_err(ithc->pci, "wrong buffer index\n"); + return -EINVAL; + } + b->active_idx = -1; + if (prds->dir == DMA_FROM_DEVICE) { + // RX buffer: Calculate actual received data size from PRDs. + dma_rmb(); // for the prds + b->data_size = 0; + struct scatterlist *sg; + int i; + for_each_sgtable_dma_sg(b->sgt, sg, i) { + unsigned int size = prd->size; + b->data_size += size & PRD_SIZE_MASK; + if (size & PRD_FLAG_END) + break; + if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) { + pci_err(ithc->pci, "truncated prd\n"); + break; + } + prd++; + } + invalidate_kernel_vmap_range(b->addr, b->data_size); + } + dma_sync_sgtable_for_cpu(&ithc->pci->dev, b->sgt, prds->dir); + return 0; +} + +int ithc_dma_rx_init(struct ithc *ithc, u8 channel) +{ + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + mutex_init(&rx->mutex); + + // Allocate buffers. + unsigned int num_pages = (ithc->max_rx_size + PAGE_SIZE - 1) / PAGE_SIZE; + pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", + NUM_RX_BUF, ithc->max_rx_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE); + for (unsigned int i = 0; i < NUM_RX_BUF; i++) + CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]); + + // Init registers. + writeb(DMA_RX_CONTROL2_RESET, &ithc->regs->dma_rx[channel].control2); + lo_hi_writeq(rx->prds.dma_addr, &ithc->regs->dma_rx[channel].addr); + writeb(NUM_RX_BUF - 1, &ithc->regs->dma_rx[channel].num_bufs); + writeb(num_pages - 1, &ithc->regs->dma_rx[channel].num_prds); + u8 head = readb(&ithc->regs->dma_rx[channel].head); + if (head) { + pci_err(ithc->pci, "head is nonzero (%u)\n", head); + return -EIO; + } + + // Init buffers. + for (unsigned int i = 0; i < NUM_RX_BUF; i++) + CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, &rx->bufs[i], i); + + writeb(head ^ DMA_RX_WRAP_FLAG, &ithc->regs->dma_rx[channel].tail); + return 0; +} + +void ithc_dma_rx_enable(struct ithc *ithc, u8 channel) +{ + bitsb_set(&ithc->regs->dma_rx[channel].control, + DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA); + CHECK(waitl, ithc, &ithc->regs->dma_rx[channel].status, + DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED); +} + +int ithc_dma_tx_init(struct ithc *ithc) +{ + struct ithc_dma_tx *tx = &ithc->dma_tx; + mutex_init(&tx->mutex); + + // Allocate buffers. + unsigned int num_pages = (ithc->max_tx_size + PAGE_SIZE - 1) / PAGE_SIZE; + pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", + ithc->max_tx_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &tx->prds, 1, num_pages, DMA_TO_DEVICE); + CHECK_RET(ithc_dma_data_alloc, ithc, &tx->prds, &tx->buf); + + // Init registers. + lo_hi_writeq(tx->prds.dma_addr, &ithc->regs->dma_tx.addr); + writeb(num_pages - 1, &ithc->regs->dma_tx.num_prds); + + // Init buffers. + CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + return 0; +} + +static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) +{ + // Process all filled RX buffers from the ringbuffer. + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + unsigned int n = rx->num_received; + u8 head_wrap = readb(&ithc->regs->dma_rx[channel].head); + while (1) { + u8 tail = n % NUM_RX_BUF; + u8 tail_wrap = tail | ((n / NUM_RX_BUF) & 1 ? 0 : DMA_RX_WRAP_FLAG); + writeb(tail_wrap, &ithc->regs->dma_rx[channel].tail); + // ringbuffer is full if tail_wrap == head_wrap + // ringbuffer is empty if tail_wrap == head_wrap ^ WRAP_FLAG + if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG)) + return 0; + + // take the buffer that the device just filled + struct ithc_dma_data_buffer *b = &rx->bufs[n % NUM_RX_BUF]; + CHECK_RET(ithc_dma_data_buffer_get, ithc, &rx->prds, b, tail); + rx->num_received = ++n; + + // process data + struct ithc_data d; + if ((ithc->use_quickspi ? ithc_quickspi_decode_rx : ithc_legacy_decode_rx) + (ithc, b->addr, b->data_size, &d) < 0) { + pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u: %*ph\n", + channel, tail, b->data_size, min((int)b->data_size, 64), b->addr); + print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, + b->addr, min(b->data_size, 0x400u), 0); + } else { + ithc_hid_process_data(ithc, &d); + } + + // give the buffer back to the device + CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail); + } +} +int ithc_dma_rx(struct ithc *ithc, u8 channel) +{ + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + mutex_lock(&rx->mutex); + int ret = ithc_dma_rx_unlocked(ithc, channel); + mutex_unlock(&rx->mutex); + return ret; +} + +static int ithc_dma_tx_unlocked(struct ithc *ithc, const struct ithc_data *data) +{ + // Send a single TX buffer to the THC. + pci_dbg(ithc->pci, "dma tx data type %u, size %u\n", data->type, data->size); + CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + + // Fill the TX buffer with header and data. + ssize_t sz; + if (data->type == ITHC_DATA_RAW) { + sz = min(data->size, ithc->max_tx_size); + memcpy(ithc->dma_tx.buf.addr, data->data, sz); + } else { + sz = (ithc->use_quickspi ? ithc_quickspi_encode_tx : ithc_legacy_encode_tx) + (ithc, data, ithc->dma_tx.buf.addr, ithc->max_tx_size); + } + ithc->dma_tx.buf.data_size = sz < 0 ? 0 : sz; + CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + if (sz < 0) { + pci_err(ithc->pci, "failed to encode tx data type %i, size %u, error %i\n", + data->type, data->size, (int)sz); + return -EINVAL; + } + + // Let the THC process the buffer. + bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND); + CHECK_RET(waitb, ithc, &ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0); + writel(DMA_TX_STATUS_DONE, &ithc->regs->dma_tx.status); + return 0; +} +int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data) +{ + mutex_lock(&ithc->dma_tx.mutex); + int ret = ithc_dma_tx_unlocked(ithc, data); + mutex_unlock(&ithc->dma_tx.mutex); + return ret; +} + diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h new file mode 100644 index 00000000000000..1749a5819b3e74 --- /dev/null +++ b/drivers/hid/ithc/ithc-dma.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +#define PRD_SIZE_MASK 0xffffff +#define PRD_FLAG_END 0x1000000 +#define PRD_FLAG_SUCCESS 0x2000000 +#define PRD_FLAG_ERROR 0x4000000 + +struct ithc_phys_region_desc { + u64 addr; // physical addr/1024 + u32 size; // num bytes, PRD_FLAG_END marks last prd for data split over multiple prds + u32 unused; +}; + +struct ithc_dma_prd_buffer { + void *addr; + dma_addr_t dma_addr; + u32 size; + u32 num_pages; // per data buffer + enum dma_data_direction dir; +}; + +struct ithc_dma_data_buffer { + void *addr; + struct sg_table *sgt; + int active_idx; + u32 data_size; +}; + +struct ithc_dma_tx { + struct mutex mutex; + struct ithc_dma_prd_buffer prds; + struct ithc_dma_data_buffer buf; +}; + +struct ithc_dma_rx { + struct mutex mutex; + u32 num_received; + struct ithc_dma_prd_buffer prds; + struct ithc_dma_data_buffer bufs[NUM_RX_BUF]; +}; + +int ithc_dma_rx_init(struct ithc *ithc, u8 channel); +void ithc_dma_rx_enable(struct ithc *ithc, u8 channel); +int ithc_dma_tx_init(struct ithc *ithc); +int ithc_dma_rx(struct ithc *ithc, u8 channel); +int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data); + diff --git a/drivers/hid/ithc/ithc-hid.c b/drivers/hid/ithc/ithc-hid.c new file mode 100644 index 00000000000000..065646ab499ef2 --- /dev/null +++ b/drivers/hid/ithc/ithc-hid.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +static int ithc_hid_start(struct hid_device *hdev) { return 0; } +static void ithc_hid_stop(struct hid_device *hdev) { } +static int ithc_hid_open(struct hid_device *hdev) { return 0; } +static void ithc_hid_close(struct hid_device *hdev) { } + +static int ithc_hid_parse(struct hid_device *hdev) +{ + struct ithc *ithc = hdev->driver_data; + const struct ithc_data get_report_desc = { .type = ITHC_DATA_REPORT_DESCRIPTOR }; + WRITE_ONCE(ithc->hid.parse_done, false); + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); + CHECK_RET(ithc_dma_tx, ithc, &get_report_desc); + if (wait_event_timeout(ithc->hid.wait_parse, READ_ONCE(ithc->hid.parse_done), + msecs_to_jiffies(200))) { + ithc_log_regs(ithc); + return 0; + } + if (retries > 5) { + ithc_log_regs(ithc); + pci_err(ithc->pci, "failed to read report descriptor\n"); + return -ETIMEDOUT; + } + pci_warn(ithc->pci, "failed to read report descriptor, retrying\n"); + } +} + +static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, + size_t len, unsigned char rtype, int reqtype) +{ + struct ithc *ithc = hdev->driver_data; + if (!buf || !len) + return -EINVAL; + + struct ithc_data d = { .size = len, .data = buf }; + buf[0] = reportnum; + + if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) { + d.type = ITHC_DATA_OUTPUT_REPORT; + CHECK_RET(ithc_dma_tx, ithc, &d); + return 0; + } + + if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) { + d.type = ITHC_DATA_SET_FEATURE; + CHECK_RET(ithc_dma_tx, ithc, &d); + return 0; + } + + if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) { + d.type = ITHC_DATA_GET_FEATURE; + d.data = &reportnum; + d.size = 1; + + // Prepare for response. + mutex_lock(&ithc->hid.get_feature_mutex); + ithc->hid.get_feature_buf = buf; + ithc->hid.get_feature_size = len; + mutex_unlock(&ithc->hid.get_feature_mutex); + + // Transmit 'get feature' request. + int r = CHECK(ithc_dma_tx, ithc, &d); + if (!r) { + r = wait_event_interruptible_timeout(ithc->hid.wait_get_feature, + !ithc->hid.get_feature_buf, msecs_to_jiffies(1000)); + if (!r) + r = -ETIMEDOUT; + else if (r < 0) + r = -EINTR; + else + r = 0; + } + + // If everything went ok, the buffer has been filled with the response data. + // Return the response size. + mutex_lock(&ithc->hid.get_feature_mutex); + ithc->hid.get_feature_buf = NULL; + if (!r) + r = ithc->hid.get_feature_size; + mutex_unlock(&ithc->hid.get_feature_mutex); + return r; + } + + pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", + rtype, reqtype, reportnum); + return -EINVAL; +} + +// FIXME hid_input_report()/hid_parse_report() currently don't take const buffers, so we have to +// cast away the const to avoid a compiler warning... +#define NOCONST(x) ((void *)x) + +void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d) +{ + WARN_ON(!ithc->hid.dev); + if (!ithc->hid.dev) + return; + + switch (d->type) { + + case ITHC_DATA_IGNORE: + return; + + case ITHC_DATA_ERROR: + CHECK(ithc_reset, ithc); + return; + + case ITHC_DATA_REPORT_DESCRIPTOR: + // Response to the report descriptor request sent by ithc_hid_parse(). + CHECK(hid_parse_report, ithc->hid.dev, NOCONST(d->data), d->size); + WRITE_ONCE(ithc->hid.parse_done, true); + wake_up(&ithc->hid.wait_parse); + return; + + case ITHC_DATA_INPUT_REPORT: + { + // Standard HID input report. + int r = hid_input_report(ithc->hid.dev, HID_INPUT_REPORT, NOCONST(d->data), d->size, 1); + if (r < 0) { + pci_warn(ithc->pci, "hid_input_report failed with %i (size %u, report ID 0x%02x)\n", + r, d->size, d->size ? *(u8 *)d->data : 0); + print_hex_dump_debug(DEVNAME " report: ", DUMP_PREFIX_OFFSET, 32, 1, + d->data, min(d->size, 0x400u), 0); + } + return; + } + + case ITHC_DATA_GET_FEATURE: + { + // Response to a 'get feature' request sent by ithc_hid_raw_request(). + bool done = false; + mutex_lock(&ithc->hid.get_feature_mutex); + if (ithc->hid.get_feature_buf) { + if (d->size < ithc->hid.get_feature_size) + ithc->hid.get_feature_size = d->size; + memcpy(ithc->hid.get_feature_buf, d->data, ithc->hid.get_feature_size); + ithc->hid.get_feature_buf = NULL; + done = true; + } + mutex_unlock(&ithc->hid.get_feature_mutex); + if (done) { + wake_up(&ithc->hid.wait_get_feature); + } else { + // Received data without a matching request, or the request already + // timed out. (XXX What's the correct thing to do here?) + CHECK(hid_input_report, ithc->hid.dev, HID_FEATURE_REPORT, + NOCONST(d->data), d->size, 1); + } + return; + } + + default: + pci_err(ithc->pci, "unhandled data type %i\n", d->type); + return; + } +} + +static struct hid_ll_driver ithc_ll_driver = { + .start = ithc_hid_start, + .stop = ithc_hid_stop, + .open = ithc_hid_open, + .close = ithc_hid_close, + .parse = ithc_hid_parse, + .raw_request = ithc_hid_raw_request, +}; + +static void ithc_hid_devres_release(struct device *dev, void *res) +{ + struct hid_device **hidm = res; + if (*hidm) + hid_destroy_device(*hidm); +} + +int ithc_hid_init(struct ithc *ithc) +{ + struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof(*hidm), GFP_KERNEL); + if (!hidm) + return -ENOMEM; + devres_add(&ithc->pci->dev, hidm); + struct hid_device *hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + *hidm = hid; + + strscpy(hid->name, DEVFULLNAME, sizeof(hid->name)); + strscpy(hid->phys, ithc->phys, sizeof(hid->phys)); + hid->ll_driver = &ithc_ll_driver; + hid->bus = BUS_PCI; + hid->vendor = ithc->vendor_id; + hid->product = ithc->product_id; + hid->version = 0x100; + hid->dev.parent = &ithc->pci->dev; + hid->driver_data = ithc; + + ithc->hid.dev = hid; + + init_waitqueue_head(&ithc->hid.wait_parse); + init_waitqueue_head(&ithc->hid.wait_get_feature); + mutex_init(&ithc->hid.get_feature_mutex); + + return 0; +} + diff --git a/drivers/hid/ithc/ithc-hid.h b/drivers/hid/ithc/ithc-hid.h new file mode 100644 index 00000000000000..599eb912c8c845 --- /dev/null +++ b/drivers/hid/ithc/ithc-hid.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +enum ithc_data_type { + ITHC_DATA_IGNORE, + ITHC_DATA_RAW, + ITHC_DATA_ERROR, + ITHC_DATA_REPORT_DESCRIPTOR, + ITHC_DATA_INPUT_REPORT, + ITHC_DATA_OUTPUT_REPORT, + ITHC_DATA_GET_FEATURE, + ITHC_DATA_SET_FEATURE, +}; + +struct ithc_data { + enum ithc_data_type type; + u32 size; + const void *data; +}; + +struct ithc_hid { + struct hid_device *dev; + bool parse_done; + wait_queue_head_t wait_parse; + wait_queue_head_t wait_get_feature; + struct mutex get_feature_mutex; + void *get_feature_buf; + size_t get_feature_size; +}; + +int ithc_hid_init(struct ithc *ithc); +void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d); + diff --git a/drivers/hid/ithc/ithc-legacy.c b/drivers/hid/ithc/ithc-legacy.c new file mode 100644 index 00000000000000..8883987fb35219 --- /dev/null +++ b/drivers/hid/ithc/ithc-legacy.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +#define DEVCFG_DMA_RX_SIZE(x) ((((x) & 0x3fff) + 1) << 6) +#define DEVCFG_DMA_TX_SIZE(x) (((((x) >> 14) & 0x3ff) + 1) << 6) + +#define DEVCFG_TOUCH_MASK 0x3f +#define DEVCFG_TOUCH_ENABLE BIT(0) +#define DEVCFG_TOUCH_PROP_DATA_ENABLE BIT(1) +#define DEVCFG_TOUCH_HID_REPORT_ENABLE BIT(2) +#define DEVCFG_TOUCH_POWER_STATE(x) (((x) & 7) << 3) +#define DEVCFG_TOUCH_UNKNOWN_6 BIT(6) + +#define DEVCFG_DEVICE_ID_TIC 0x43495424 // "$TIC" + +#define DEVCFG_SPI_CLKDIV(x) (((x) >> 1) & 7) +#define DEVCFG_SPI_CLKDIV_8 BIT(4) +#define DEVCFG_SPI_SUPPORTS_SINGLE BIT(5) +#define DEVCFG_SPI_SUPPORTS_DUAL BIT(6) +#define DEVCFG_SPI_SUPPORTS_QUAD BIT(7) +#define DEVCFG_SPI_MAX_TOUCH_POINTS(x) (((x) >> 8) & 0x3f) +#define DEVCFG_SPI_MIN_RESET_TIME(x) (((x) >> 16) & 0xf) +#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat +#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((x) >> 21) & 7) +#define DEVCFG_SPI_UNKNOWN_25 BIT(25) +#define DEVCFG_SPI_UNKNOWN_26 BIT(26) +#define DEVCFG_SPI_UNKNOWN_27 BIT(27) +#define DEVCFG_SPI_DELAY(x) (((x) >> 28) & 7) // TODO use this +#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this? + +struct ithc_device_config { // (Example values are from an SP7+.) + u32 irq_cause; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET) + u32 error; // 04 = 0x00000000 + u32 dma_buf_sizes; // 08 = 0x000a00ff + u32 touch_cfg; // 0c = 0x0000001c + u32 touch_state; // 10 = 0x0000001c + u32 device_id; // 14 = 0x43495424 = "$TIC" + u32 spi_config; // 18 = 0xfda00a2e + u16 vendor_id; // 1c = 0x045e = Microsoft Corp. + u16 product_id; // 1e = 0x0c1a + u32 revision; // 20 = 0x00000001 + u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 (this value looks more random on newer devices) + u32 command; // 28 = 0x00000000 + u32 fw_mode; // 2c = 0x00000000 (for fw update?) + u32 _unknown_30; // 30 = 0x00000000 + u8 eds_minor_ver; // 34 = 0x5e + u8 eds_major_ver; // 35 = 0x03 + u8 interface_rev; // 36 = 0x04 + u8 eu_kernel_ver; // 37 = 0x04 + u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET) + u32 _unknown_3c; // 3c = 0x00000002 +}; +static_assert(sizeof(struct ithc_device_config) == 64); + +#define RX_CODE_INPUT_REPORT 3 +#define RX_CODE_FEATURE_REPORT 4 +#define RX_CODE_REPORT_DESCRIPTOR 5 +#define RX_CODE_RESET 7 + +#define TX_CODE_SET_FEATURE 3 +#define TX_CODE_GET_FEATURE 4 +#define TX_CODE_OUTPUT_REPORT 5 +#define TX_CODE_GET_REPORT_DESCRIPTOR 7 + +static int ithc_set_device_enabled(struct ithc *ithc, bool enable) +{ + u32 x = ithc->legacy_touch_cfg = + (ithc->legacy_touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | + DEVCFG_TOUCH_HID_REPORT_ENABLE | + (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_POWER_STATE(3) : 0); + return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, + offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x); +} + +int ithc_legacy_init(struct ithc *ithc) +{ + // Since we don't yet know which SPI config the device wants, use default speed and mode + // initially for reading config data. + CHECK(ithc_set_spi_config, ithc, 2, true, SPI_MODE_SINGLE, SPI_MODE_SINGLE); + + // Setting the following bit seems to make reading the config more reliable. + bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); + + // Setting this bit may be necessary on ADL devices. + switch (ithc->pci->device) { + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2: + bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_5); + break; + } + + // Take the touch device out of reset. + bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0); + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); + bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET); + if (!waitl(ithc, &ithc->regs->irq_cause, 0xf, 2)) + break; + if (retries > 5) { + pci_err(ithc->pci, "failed to reset device, irq_cause = 0x%08x\n", + readl(&ithc->regs->irq_cause)); + return -ETIMEDOUT; + } + pci_warn(ithc->pci, "invalid irq_cause, retrying reset\n"); + bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); + if (msleep_interruptible(1000)) + return -EINTR; + } + ithc_log_regs(ithc); + + CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_READY, DMA_RX_STATUS_READY); + + // Read configuration data. + u32 spi_cfg; + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); + struct ithc_device_config config = { 0 }; + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(config), &config); + u32 *p = (void *)&config; + pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + if (config.device_id == DEVCFG_DEVICE_ID_TIC) { + spi_cfg = config.spi_config; + ithc->vendor_id = config.vendor_id; + ithc->product_id = config.product_id; + ithc->product_rev = config.revision; + ithc->max_rx_size = DEVCFG_DMA_RX_SIZE(config.dma_buf_sizes); + ithc->max_tx_size = DEVCFG_DMA_TX_SIZE(config.dma_buf_sizes); + ithc->legacy_touch_cfg = config.touch_cfg; + ithc->have_config = true; + break; + } + if (retries > 10) { + pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", + config.device_id); + return -EIO; + } + pci_warn(ithc->pci, "failed to read config, retrying\n"); + if (msleep_interruptible(100)) + return -EINTR; + } + ithc_log_regs(ithc); + + // Apply SPI config and enable touch device. + CHECK_RET(ithc_set_spi_config, ithc, + DEVCFG_SPI_CLKDIV(spi_cfg), (spi_cfg & DEVCFG_SPI_CLKDIV_8) != 0, + spi_cfg & DEVCFG_SPI_SUPPORTS_QUAD ? SPI_MODE_QUAD : + spi_cfg & DEVCFG_SPI_SUPPORTS_DUAL ? SPI_MODE_DUAL : + SPI_MODE_SINGLE, + SPI_MODE_SINGLE); + CHECK_RET(ithc_set_device_enabled, ithc, true); + ithc_log_regs(ithc); + return 0; +} + +void ithc_legacy_exit(struct ithc *ithc) +{ + CHECK(ithc_set_device_enabled, ithc, false); +} + +int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest) +{ + const struct { + u32 code; + u32 data_size; + u32 _unknown[14]; + } *hdr = src; + + if (len < sizeof(*hdr)) + return -ENODATA; + // Note: RX data is not padded, even though TX data must be padded. + if (len != sizeof(*hdr) + hdr->data_size) + return -EMSGSIZE; + + dest->data = hdr + 1; + dest->size = hdr->data_size; + + switch (hdr->code) { + case RX_CODE_RESET: + // The THC sends a reset request when we need to reinitialize the device. + // This usually only happens if we send an invalid command or put the device + // in a bad state. + dest->type = ITHC_DATA_ERROR; + return 0; + case RX_CODE_REPORT_DESCRIPTOR: + // The descriptor is preceded by 8 nul bytes. + if (hdr->data_size < 8) + return -ENODATA; + dest->type = ITHC_DATA_REPORT_DESCRIPTOR; + dest->data = (char *)(hdr + 1) + 8; + dest->size = hdr->data_size - 8; + return 0; + case RX_CODE_INPUT_REPORT: + dest->type = ITHC_DATA_INPUT_REPORT; + return 0; + case RX_CODE_FEATURE_REPORT: + dest->type = ITHC_DATA_GET_FEATURE; + return 0; + default: + return -EINVAL; + } +} + +ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen) +{ + struct { + u32 code; + u32 data_size; + } *hdr = dest; + + size_t src_size = src->size; + const void *src_data = src->data; + const u64 get_report_desc_data = 0; + u32 code; + + switch (src->type) { + case ITHC_DATA_SET_FEATURE: + code = TX_CODE_SET_FEATURE; + break; + case ITHC_DATA_GET_FEATURE: + code = TX_CODE_GET_FEATURE; + break; + case ITHC_DATA_OUTPUT_REPORT: + code = TX_CODE_OUTPUT_REPORT; + break; + case ITHC_DATA_REPORT_DESCRIPTOR: + code = TX_CODE_GET_REPORT_DESCRIPTOR; + src_size = sizeof(get_report_desc_data); + src_data = &get_report_desc_data; + break; + default: + return -EINVAL; + } + + // Data must be padded to next 4-byte boundary. + size_t padded = round_up(src_size, 4); + if (sizeof(*hdr) + padded > maxlen) + return -EOVERFLOW; + + // Fill the TX buffer with header and data. + hdr->code = code; + hdr->data_size = src_size; + memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0); + + return sizeof(*hdr) + padded; +} + diff --git a/drivers/hid/ithc/ithc-legacy.h b/drivers/hid/ithc/ithc-legacy.h new file mode 100644 index 00000000000000..28d69246207227 --- /dev/null +++ b/drivers/hid/ithc/ithc-legacy.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +int ithc_legacy_init(struct ithc *ithc); +void ithc_legacy_exit(struct ithc *ithc); +int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest); +ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen); + diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c new file mode 100644 index 00000000000000..094d878d671b31 --- /dev/null +++ b/drivers/hid/ithc/ithc-main.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +MODULE_DESCRIPTION("Intel Touch Host Controller driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static const struct pci_device_id ithc_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2) }, + // MTL and up are handled by drivers/hid/intel-thc-hid + {} +}; +MODULE_DEVICE_TABLE(pci, ithc_pci_tbl); + +// Module parameters + +static bool ithc_use_polling = false; +module_param_named(poll, ithc_use_polling, bool, 0); +MODULE_PARM_DESC(poll, "Use polling instead of interrupts"); + +// Since all known devices seem to use only channel 1, by default we disable channel 0. +static bool ithc_use_rx0 = false; +module_param_named(rx0, ithc_use_rx0, bool, 0); +MODULE_PARM_DESC(rx0, "Use DMA RX channel 0"); + +static bool ithc_use_rx1 = true; +module_param_named(rx1, ithc_use_rx1, bool, 0); +MODULE_PARM_DESC(rx1, "Use DMA RX channel 1"); + +static int ithc_active_ltr_us = -1; +module_param_named(activeltr, ithc_active_ltr_us, int, 0); +MODULE_PARM_DESC(activeltr, "Active LTR value override (in microseconds)"); + +static int ithc_idle_ltr_us = -1; +module_param_named(idleltr, ithc_idle_ltr_us, int, 0); +MODULE_PARM_DESC(idleltr, "Idle LTR value override (in microseconds)"); + +static unsigned int ithc_idle_delay_ms = 1000; +module_param_named(idledelay, ithc_idle_delay_ms, uint, 0); +MODULE_PARM_DESC(idleltr, "Minimum idle time before applying idle LTR value (in milliseconds)"); + +static bool ithc_log_regs_enabled = false; +module_param_named(logregs, ithc_log_regs_enabled, bool, 0); +MODULE_PARM_DESC(logregs, "Log changes in register values (for debugging)"); + +// Interrupts/polling + +static void ithc_disable_interrupts(struct ithc *ithc) +{ + writel(0, &ithc->regs->error_control); + bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_IRQ, 0); + bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_READY | DMA_RX_CONTROL_IRQ_DATA, 0); + bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_READY | DMA_RX_CONTROL_IRQ_DATA, 0); + bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_IRQ, 0); +} + +static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned int channel) +{ + writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_READY | DMA_RX_STATUS_HAVE_DATA, + &ithc->regs->dma_rx[channel].status); +} + +static void ithc_clear_interrupts(struct ithc *ithc) +{ + writel(0xffffffff, &ithc->regs->error_flags); + writel(ERROR_STATUS_DMA | ERROR_STATUS_SPI, &ithc->regs->error_status); + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + ithc_clear_dma_rx_interrupts(ithc, 0); + ithc_clear_dma_rx_interrupts(ithc, 1); + writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2, + &ithc->regs->dma_tx.status); +} + +static void ithc_idle_timer_callback(struct timer_list *t) +{ + struct ithc *ithc = container_of(t, struct ithc, idle_timer); + ithc_set_ltr_idle(ithc); +} + +static void ithc_process(struct ithc *ithc) +{ + ithc_log_regs(ithc); + + // The THC automatically transitions from LTR idle to active at the start of a DMA transfer. + // It does not appear to automatically go back to idle, so we switch it back after a delay. + mod_timer(&ithc->idle_timer, jiffies + msecs_to_jiffies(ithc_idle_delay_ms)); + + bool rx0 = ithc_use_rx0 && (readl(&ithc->regs->dma_rx[0].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; + bool rx1 = ithc_use_rx1 && (readl(&ithc->regs->dma_rx[1].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; + + // Read and clear error bits + u32 err = readl(&ithc->regs->error_flags); + if (err) { + writel(err, &ithc->regs->error_flags); + if (err & ~ERROR_FLAG_DMA_RX_TIMEOUT) + pci_err(ithc->pci, "error flags: 0x%08x\n", err); + if (err & ERROR_FLAG_DMA_RX_TIMEOUT) + pci_err(ithc->pci, "DMA RX timeout/error (try decreasing activeltr/idleltr if this happens frequently)\n"); + } + + // Process DMA rx + if (ithc_use_rx0) { + ithc_clear_dma_rx_interrupts(ithc, 0); + if (rx0) + ithc_dma_rx(ithc, 0); + } + if (ithc_use_rx1) { + ithc_clear_dma_rx_interrupts(ithc, 1); + if (rx1) + ithc_dma_rx(ithc, 1); + } + + ithc_log_regs(ithc); +} + +static irqreturn_t ithc_interrupt_thread(int irq, void *arg) +{ + struct ithc *ithc = arg; + pci_dbg(ithc->pci, "IRQ! err=%08x/%08x/%08x, cmd=%02x/%08x, rx0=%02x/%08x, rx1=%02x/%08x, tx=%02x/%08x\n", + readl(&ithc->regs->error_control), readl(&ithc->regs->error_status), readl(&ithc->regs->error_flags), + readb(&ithc->regs->spi_cmd.control), readl(&ithc->regs->spi_cmd.status), + readb(&ithc->regs->dma_rx[0].control), readl(&ithc->regs->dma_rx[0].status), + readb(&ithc->regs->dma_rx[1].control), readl(&ithc->regs->dma_rx[1].status), + readb(&ithc->regs->dma_tx.control), readl(&ithc->regs->dma_tx.status)); + ithc_process(ithc); + return IRQ_HANDLED; +} + +static int ithc_poll_thread(void *arg) +{ + struct ithc *ithc = arg; + unsigned int sleep = 100; + while (!kthread_should_stop()) { + u32 n = ithc->dma_rx[1].num_received; + ithc_process(ithc); + // Decrease polling interval to 20ms if we received data, otherwise slowly + // increase it up to 200ms. + sleep = n != ithc->dma_rx[1].num_received ? 20 + : min(200u, sleep + (sleep >> 4) + 1); + msleep_interruptible(sleep); + } + return 0; +} + +// Device initialization and shutdown + +static void ithc_disable(struct ithc *ithc) +{ + bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE); + CHECK(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED); + bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); + bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND, 0); + bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0); + bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_ENABLE, 0); + bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_ENABLE, 0); + ithc_disable_interrupts(ithc); + ithc_clear_interrupts(ithc); +} + +static int ithc_init_device(struct ithc *ithc) +{ + // Read ACPI config for QuickSPI mode + struct ithc_acpi_config cfg = { 0 }; + CHECK_RET(ithc_read_acpi_config, ithc, &cfg); + if (!cfg.has_config) + pci_info(ithc->pci, "no ACPI config, using legacy mode\n"); + else + ithc_print_acpi_config(ithc, &cfg); + ithc->use_quickspi = cfg.has_config; + + // Shut down device + ithc_log_regs(ithc); + bool was_enabled = (readl(&ithc->regs->control_bits) & CONTROL_NRESET) != 0; + ithc_disable(ithc); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_READY, CONTROL_READY); + ithc_log_regs(ithc); + + // If the device was previously enabled, wait a bit to make sure it's fully shut down. + if (was_enabled) + if (msleep_interruptible(100)) + return -EINTR; + + // Set Latency Tolerance Reporting config. The device will automatically + // apply these values depending on whether it is active or idle. + // If active value is too high, DMA buffer data can become truncated. + // By default, we set the active LTR value to 50us, and idle to 100ms. + u64 active_ltr_ns = ithc_active_ltr_us >= 0 ? (u64)ithc_active_ltr_us * 1000 + : cfg.has_config && cfg.has_active_ltr ? (u64)cfg.active_ltr << 10 + : 50 * 1000; + u64 idle_ltr_ns = ithc_idle_ltr_us >= 0 ? (u64)ithc_idle_ltr_us * 1000 + : cfg.has_config && cfg.has_idle_ltr ? (u64)cfg.idle_ltr << 10 + : 100 * 1000 * 1000; + ithc_set_ltr_config(ithc, active_ltr_ns, idle_ltr_ns); + + if (ithc->use_quickspi) + CHECK_RET(ithc_quickspi_init, ithc, &cfg); + else + CHECK_RET(ithc_legacy_init, ithc); + + return 0; +} + +int ithc_reset(struct ithc *ithc) +{ + // FIXME This should probably do devres_release_group()+ithc_start(). + // But because this is called during DMA processing, that would have to be done + // asynchronously (schedule_work()?). And with extra locking? + pci_err(ithc->pci, "reset\n"); + CHECK(ithc_init_device, ithc); + if (ithc_use_rx0) + ithc_dma_rx_enable(ithc, 0); + if (ithc_use_rx1) + ithc_dma_rx_enable(ithc, 1); + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "reset completed\n"); + return 0; +} + +static void ithc_stop(void *res) +{ + struct ithc *ithc = res; + pci_dbg(ithc->pci, "stopping\n"); + ithc_log_regs(ithc); + + if (ithc->poll_thread) + CHECK(kthread_stop, ithc->poll_thread); + if (ithc->irq >= 0) + disable_irq(ithc->irq); + if (ithc->use_quickspi) + ithc_quickspi_exit(ithc); + else + ithc_legacy_exit(ithc); + ithc_disable(ithc); + timer_delete_sync(&ithc->idle_timer); + + // Clear DMA config. + for (unsigned int i = 0; i < 2; i++) { + CHECK(waitl, ithc, &ithc->regs->dma_rx[i].status, DMA_RX_STATUS_ENABLED, 0); + lo_hi_writeq(0, &ithc->regs->dma_rx[i].addr); + writeb(0, &ithc->regs->dma_rx[i].num_bufs); + writeb(0, &ithc->regs->dma_rx[i].num_prds); + } + lo_hi_writeq(0, &ithc->regs->dma_tx.addr); + writeb(0, &ithc->regs->dma_tx.num_prds); + + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "stopped\n"); +} + +static void ithc_clear_drvdata(void *res) +{ + struct pci_dev *pci = res; + pci_set_drvdata(pci, NULL); +} + +static int ithc_start(struct pci_dev *pci) +{ + pci_dbg(pci, "starting\n"); + if (pci_get_drvdata(pci)) { + pci_err(pci, "device already initialized\n"); + return -EINVAL; + } + if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL)) + return -ENOMEM; + + // Allocate/init main driver struct. + struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof(*ithc), GFP_KERNEL); + if (!ithc) + return -ENOMEM; + ithc->irq = -1; + ithc->pci = pci; + snprintf(ithc->phys, sizeof(ithc->phys), "pci-%s/" DEVNAME, pci_name(pci)); + pci_set_drvdata(pci, ithc); + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_clear_drvdata, pci); + if (ithc_log_regs_enabled) + ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof(*ithc->prev_regs), GFP_KERNEL); + + // PCI initialization. + CHECK_RET(pcim_enable_device, pci); + pci_set_master(pci); + CHECK_RET(pcim_iomap_regions, pci, BIT(0), DEVNAME " regs"); + CHECK_RET(dma_set_mask_and_coherent, &pci->dev, DMA_BIT_MASK(64)); + CHECK_RET(pci_set_power_state, pci, PCI_D0); + ithc->regs = pcim_iomap_table(pci)[0]; + + // Allocate IRQ. + if (!ithc_use_polling) { + CHECK_RET(pci_alloc_irq_vectors, pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX); + ithc->irq = CHECK(pci_irq_vector, pci, 0); + if (ithc->irq < 0) + return ithc->irq; + } + + // Initialize THC and touch device. + CHECK_RET(ithc_init_device, ithc); + + // Initialize HID and DMA. + CHECK_RET(ithc_hid_init, ithc); + if (ithc_use_rx0) + CHECK_RET(ithc_dma_rx_init, ithc, 0); + if (ithc_use_rx1) + CHECK_RET(ithc_dma_rx_init, ithc, 1); + CHECK_RET(ithc_dma_tx_init, ithc); + + timer_setup(&ithc->idle_timer, ithc_idle_timer_callback, 0); + + // Add ithc_stop() callback AFTER setting up DMA buffers, so that polling/irqs/DMA are + // disabled BEFORE the buffers are freed. + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_stop, ithc); + + // Start polling/IRQ. + if (ithc_use_polling) { + pci_info(pci, "using polling instead of irq\n"); + // Use a thread instead of simple timer because we want to be able to sleep. + ithc->poll_thread = kthread_run(ithc_poll_thread, ithc, DEVNAME "poll"); + if (IS_ERR(ithc->poll_thread)) { + int err = PTR_ERR(ithc->poll_thread); + ithc->poll_thread = NULL; + return err; + } + } else { + CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL, + ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc); + } + + if (ithc_use_rx0) + ithc_dma_rx_enable(ithc, 0); + if (ithc_use_rx1) + ithc_dma_rx_enable(ithc, 1); + + // hid_add_device() can only be called after irq/polling is started and DMA is enabled, + // because it calls ithc_hid_parse() which reads the report descriptor via DMA. + CHECK_RET(hid_add_device, ithc->hid.dev); + + CHECK(ithc_debug_init_device, ithc); + + ithc_set_ltr_idle(ithc); + + pci_dbg(pci, "started\n"); + return 0; +} + +static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + pci_dbg(pci, "device probe\n"); + return ithc_start(pci); +} + +static void ithc_remove(struct pci_dev *pci) +{ + pci_dbg(pci, "device remove\n"); + // all cleanup is handled by devres +} + +// For suspend/resume, we just deinitialize and reinitialize everything. +// TODO It might be cleaner to keep the HID device around, however we would then have to signal +// to userspace that the touch device has lost state and userspace needs to e.g. resend 'set +// feature' requests. Hidraw does not seem to have a facility to do that. +static int ithc_suspend(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm suspend\n"); + devres_release_group(dev, ithc_start); + return 0; +} + +static int ithc_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm resume\n"); + return ithc_start(pci); +} + +static int ithc_freeze(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm freeze\n"); + devres_release_group(dev, ithc_start); + return 0; +} + +static int ithc_thaw(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm thaw\n"); + return ithc_start(pci); +} + +static int ithc_restore(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm restore\n"); + return ithc_start(pci); +} + +static struct pci_driver ithc_driver = { + .name = DEVNAME, + .id_table = ithc_pci_tbl, + .probe = ithc_probe, + .remove = ithc_remove, + .driver.pm = &(const struct dev_pm_ops) { + .suspend = ithc_suspend, + .resume = ithc_resume, + .freeze = ithc_freeze, + .thaw = ithc_thaw, + .restore = ithc_restore, + }, + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, +}; + +static int __init ithc_init(void) +{ + ithc_debug_init_module(); + return pci_register_driver(&ithc_driver); +} + +static void __exit ithc_exit(void) +{ + pci_unregister_driver(&ithc_driver); + ithc_debug_exit_module(); +} + +module_init(ithc_init); +module_exit(ithc_exit); + diff --git a/drivers/hid/ithc/ithc-quickspi.c b/drivers/hid/ithc/ithc-quickspi.c new file mode 100644 index 00000000000000..e2d1690b8cf8c1 --- /dev/null +++ b/drivers/hid/ithc/ithc-quickspi.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +// Some public THC/QuickSPI documentation can be found in: +// - Intel Firmware Support Package repo: https://github.com/intel/FSP +// - HID over SPI (HIDSPI) spec: https://www.microsoft.com/en-us/download/details.aspx?id=103325 + +#include "ithc.h" + +static const guid_t guid_hidspi = + GUID_INIT(0x6e2ac436, 0x0fcf, 0x41af, 0xa2, 0x65, 0xb3, 0x2a, 0x22, 0x0d, 0xcf, 0xab); +static const guid_t guid_thc_quickspi = + GUID_INIT(0x300d35b7, 0xac20, 0x413e, 0x8e, 0x9c, 0x92, 0xe4, 0xda, 0xfd, 0x0a, 0xfe); +static const guid_t guid_thc_ltr = + GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); + +// TODO The HIDSPI spec says revision should be 3. Should we try both? +#define DSM_REV 2 + +struct hidspi_header { + u8 type; + u16 len; + u8 id; +} __packed; +static_assert(sizeof(struct hidspi_header) == 4); + +#define HIDSPI_INPUT_TYPE_DATA 1 +#define HIDSPI_INPUT_TYPE_RESET_RESPONSE 3 +#define HIDSPI_INPUT_TYPE_COMMAND_RESPONSE 4 +#define HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE 5 +#define HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR 7 +#define HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR 8 +#define HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE 9 +#define HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE 10 +#define HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE 11 + +#define HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST 1 +#define HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST 2 +#define HIDSPI_OUTPUT_TYPE_SET_FEATURE 3 +#define HIDSPI_OUTPUT_TYPE_GET_FEATURE 4 +#define HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT 5 +#define HIDSPI_OUTPUT_TYPE_INPUT_REPORT_REQUEST 6 +#define HIDSPI_OUTPUT_TYPE_COMMAND 7 + +struct hidspi_device_descriptor { + u16 wDeviceDescLength; + u16 bcdVersion; + u16 wReportDescLength; + u16 wMaxInputLength; + u16 wMaxOutputLength; + u16 wMaxFragmentLength; + u16 wVendorID; + u16 wProductID; + u16 wVersionID; + u16 wFlags; + u32 dwReserved; +}; +static_assert(sizeof(struct hidspi_device_descriptor) == 24); + +static int read_acpi_u32(struct ithc *ithc, const guid_t *guid, u32 func, u32 *dest) +{ + acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); + union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL); + if (!o) + return 0; + if (o->type != ACPI_TYPE_INTEGER) { + pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of integer\n", + guid, func, o->type); + ACPI_FREE(o); + return -1; + } + pci_dbg(ithc->pci, "DSM %pUl %u = 0x%08x\n", guid, func, (u32)o->integer.value); + *dest = (u32)o->integer.value; + ACPI_FREE(o); + return 1; +} + +static int read_acpi_buf(struct ithc *ithc, const guid_t *guid, u32 func, size_t len, u8 *dest) +{ + acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); + union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL); + if (!o) + return 0; + if (o->type != ACPI_TYPE_BUFFER) { + pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of buffer\n", + guid, func, o->type); + ACPI_FREE(o); + return -1; + } + if (o->buffer.length != len) { + pci_err(ithc->pci, "DSM %pUl %u returned len %u instead of %zu\n", + guid, func, o->buffer.length, len); + ACPI_FREE(o); + return -1; + } + memcpy(dest, o->buffer.pointer, len); + pci_dbg(ithc->pci, "DSM %pUl %u = 0x%02x\n", guid, func, dest[0]); + ACPI_FREE(o); + return 1; +} + +int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg) +{ + int r; + acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); + + cfg->has_config = acpi_check_dsm(handle, &guid_hidspi, DSM_REV, BIT(0)); + if (!cfg->has_config) + return 0; + + // HIDSPI settings + + r = read_acpi_u32(ithc, &guid_hidspi, 1, &cfg->input_report_header_address); + if (r < 0) + return r; + cfg->has_input_report_header_address = r > 0; + if (r > 0 && cfg->input_report_header_address > 0xffffff) { + pci_err(ithc->pci, "Invalid input report header address 0x%x\n", + cfg->input_report_header_address); + return -1; + } + + r = read_acpi_u32(ithc, &guid_hidspi, 2, &cfg->input_report_body_address); + if (r < 0) + return r; + cfg->has_input_report_body_address = r > 0; + if (r > 0 && cfg->input_report_body_address > 0xffffff) { + pci_err(ithc->pci, "Invalid input report body address 0x%x\n", + cfg->input_report_body_address); + return -1; + } + + r = read_acpi_u32(ithc, &guid_hidspi, 3, &cfg->output_report_body_address); + if (r < 0) + return r; + cfg->has_output_report_body_address = r > 0; + if (r > 0 && cfg->output_report_body_address > 0xffffff) { + pci_err(ithc->pci, "Invalid output report body address 0x%x\n", + cfg->output_report_body_address); + return -1; + } + + r = read_acpi_buf(ithc, &guid_hidspi, 4, sizeof(cfg->read_opcode), &cfg->read_opcode); + if (r < 0) + return r; + cfg->has_read_opcode = r > 0; + + r = read_acpi_buf(ithc, &guid_hidspi, 5, sizeof(cfg->write_opcode), &cfg->write_opcode); + if (r < 0) + return r; + cfg->has_write_opcode = r > 0; + + u32 flags; + r = read_acpi_u32(ithc, &guid_hidspi, 6, &flags); + if (r < 0) + return r; + cfg->has_read_mode = cfg->has_write_mode = r > 0; + if (r > 0) { + cfg->read_mode = (flags >> 14) & 3; + cfg->write_mode = flags & BIT(13) ? cfg->read_mode : SPI_MODE_SINGLE; + } + + // Quick SPI settings + + r = read_acpi_u32(ithc, &guid_thc_quickspi, 1, &cfg->spi_frequency); + if (r < 0) + return r; + cfg->has_spi_frequency = r > 0; + + r = read_acpi_u32(ithc, &guid_thc_quickspi, 2, &cfg->limit_packet_size); + if (r < 0) + return r; + cfg->has_limit_packet_size = r > 0; + + r = read_acpi_u32(ithc, &guid_thc_quickspi, 3, &cfg->tx_delay); + if (r < 0) + return r; + cfg->has_tx_delay = r > 0; + if (r > 0) + cfg->tx_delay &= 0xffff; + + // LTR settings + + r = read_acpi_u32(ithc, &guid_thc_ltr, 1, &cfg->active_ltr); + if (r < 0) + return r; + cfg->has_active_ltr = r > 0; + if (r > 0 && (!cfg->active_ltr || cfg->active_ltr > 0x3ff)) { + if (cfg->active_ltr != 0xffffffff) + pci_warn(ithc->pci, "Ignoring invalid active LTR value 0x%x\n", + cfg->active_ltr); + cfg->active_ltr = 500; + } + + r = read_acpi_u32(ithc, &guid_thc_ltr, 2, &cfg->idle_ltr); + if (r < 0) + return r; + cfg->has_idle_ltr = r > 0; + if (r > 0 && (!cfg->idle_ltr || cfg->idle_ltr > 0x3ff)) { + if (cfg->idle_ltr != 0xffffffff) + pci_warn(ithc->pci, "Ignoring invalid idle LTR value 0x%x\n", + cfg->idle_ltr); + cfg->idle_ltr = 500; + if (cfg->has_active_ltr && cfg->active_ltr > cfg->idle_ltr) + cfg->idle_ltr = cfg->active_ltr; + } + + return 0; +} + +void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + if (!cfg->has_config) { + pci_info(ithc->pci, "No ACPI config"); + return; + } + + char input_report_header_address[16] = "-"; + if (cfg->has_input_report_header_address) + sprintf(input_report_header_address, "0x%x", cfg->input_report_header_address); + char input_report_body_address[16] = "-"; + if (cfg->has_input_report_body_address) + sprintf(input_report_body_address, "0x%x", cfg->input_report_body_address); + char output_report_body_address[16] = "-"; + if (cfg->has_output_report_body_address) + sprintf(output_report_body_address, "0x%x", cfg->output_report_body_address); + char read_opcode[16] = "-"; + if (cfg->has_read_opcode) + sprintf(read_opcode, "0x%02x", cfg->read_opcode); + char write_opcode[16] = "-"; + if (cfg->has_write_opcode) + sprintf(write_opcode, "0x%02x", cfg->write_opcode); + char read_mode[16] = "-"; + if (cfg->has_read_mode) + sprintf(read_mode, "%i", cfg->read_mode); + char write_mode[16] = "-"; + if (cfg->has_write_mode) + sprintf(write_mode, "%i", cfg->write_mode); + char spi_frequency[16] = "-"; + if (cfg->has_spi_frequency) + sprintf(spi_frequency, "%u", cfg->spi_frequency); + char limit_packet_size[16] = "-"; + if (cfg->has_limit_packet_size) + sprintf(limit_packet_size, "%u", cfg->limit_packet_size); + char tx_delay[16] = "-"; + if (cfg->has_tx_delay) + sprintf(tx_delay, "%u", cfg->tx_delay); + char active_ltr[16] = "-"; + if (cfg->has_active_ltr) + sprintf(active_ltr, "%u", cfg->active_ltr); + char idle_ltr[16] = "-"; + if (cfg->has_idle_ltr) + sprintf(idle_ltr, "%u", cfg->idle_ltr); + + pci_info(ithc->pci, "ACPI config: InputHeaderAddr=%s InputBodyAddr=%s OutputBodyAddr=%s ReadOpcode=%s WriteOpcode=%s ReadMode=%s WriteMode=%s Frequency=%s LimitPacketSize=%s TxDelay=%s ActiveLTR=%s IdleLTR=%s\n", + input_report_header_address, input_report_body_address, output_report_body_address, + read_opcode, write_opcode, read_mode, write_mode, + spi_frequency, limit_packet_size, tx_delay, active_ltr, idle_ltr); +} + +static void set_opcode(struct ithc *ithc, size_t i, u8 opcode) +{ + writeb(opcode, &ithc->regs->opcode[i].header); + writeb(opcode, &ithc->regs->opcode[i].single); + writeb(opcode, &ithc->regs->opcode[i].dual); + writeb(opcode, &ithc->regs->opcode[i].quad); +} + +static int ithc_quickspi_init_regs(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + pci_dbg(ithc->pci, "initializing QuickSPI registers\n"); + + // SPI frequency and mode + if (!cfg->has_spi_frequency || !cfg->spi_frequency) { + pci_err(ithc->pci, "Missing SPI frequency in configuration\n"); + return -EINVAL; + } + unsigned int clkdiv = DIV_ROUND_UP(SPI_CLK_FREQ_BASE, cfg->spi_frequency); + bool clkdiv8 = clkdiv > 7; + if (clkdiv8) + clkdiv = min(7u, DIV_ROUND_UP(clkdiv, 8u)); + if (!clkdiv) + clkdiv = 1; + CHECK_RET(ithc_set_spi_config, ithc, clkdiv, clkdiv8, + cfg->has_read_mode ? cfg->read_mode : SPI_MODE_SINGLE, + cfg->has_write_mode ? cfg->write_mode : SPI_MODE_SINGLE); + + // SPI addresses and opcodes + if (cfg->has_input_report_header_address) + writel(cfg->input_report_header_address, &ithc->regs->spi_header_addr); + if (cfg->has_input_report_body_address) { + writel(cfg->input_report_body_address, &ithc->regs->dma_rx[0].spi_addr); + writel(cfg->input_report_body_address, &ithc->regs->dma_rx[1].spi_addr); + } + if (cfg->has_output_report_body_address) + writel(cfg->output_report_body_address, &ithc->regs->dma_tx.spi_addr); + + switch (ithc->pci->device) { + // LKF/TGL don't support QuickSPI. + // For ADL, opcode layout is RX/TX/unused. + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2: + if (cfg->has_read_opcode) { + set_opcode(ithc, 0, cfg->read_opcode); + } + if (cfg->has_write_opcode) { + set_opcode(ithc, 1, cfg->write_opcode); + } + break; + // For MTL, opcode layout was changed to RX/RX/TX. + // (RPL layout is unknown.) + default: + if (cfg->has_read_opcode) { + set_opcode(ithc, 0, cfg->read_opcode); + set_opcode(ithc, 1, cfg->read_opcode); + } + if (cfg->has_write_opcode) { + set_opcode(ithc, 2, cfg->write_opcode); + } + break; + } + + ithc_log_regs(ithc); + + // The rest... + bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); + + bitsl(&ithc->regs->quickspi_config1, + QUICKSPI_CONFIG1_UNKNOWN_0(0xff) | QUICKSPI_CONFIG1_UNKNOWN_5(0xff) | + QUICKSPI_CONFIG1_UNKNOWN_10(0xff) | QUICKSPI_CONFIG1_UNKNOWN_16(0xffff), + QUICKSPI_CONFIG1_UNKNOWN_0(4) | QUICKSPI_CONFIG1_UNKNOWN_5(4) | + QUICKSPI_CONFIG1_UNKNOWN_10(22) | QUICKSPI_CONFIG1_UNKNOWN_16(2)); + + bitsl(&ithc->regs->quickspi_config2, + QUICKSPI_CONFIG2_UNKNOWN_0(0xff) | QUICKSPI_CONFIG2_UNKNOWN_5(0xff) | + QUICKSPI_CONFIG2_UNKNOWN_12(0xff), + QUICKSPI_CONFIG2_UNKNOWN_0(8) | QUICKSPI_CONFIG2_UNKNOWN_5(14) | + QUICKSPI_CONFIG2_UNKNOWN_12(2)); + + u32 pktsize = cfg->has_limit_packet_size && cfg->limit_packet_size == 1 ? 4 : 0x80; + bitsl(&ithc->regs->spi_config, + SPI_CONFIG_READ_PACKET_SIZE(0xfff) | SPI_CONFIG_WRITE_PACKET_SIZE(0xfff), + SPI_CONFIG_READ_PACKET_SIZE(pktsize) | SPI_CONFIG_WRITE_PACKET_SIZE(pktsize)); + + bitsl_set(&ithc->regs->quickspi_config2, + QUICKSPI_CONFIG2_UNKNOWN_16 | QUICKSPI_CONFIG2_UNKNOWN_17); + bitsl(&ithc->regs->quickspi_config2, + QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT | + QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT | + QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE, 0); + + return 0; +} + +static int wait_for_report(struct ithc *ithc) +{ + CHECK_RET(waitl, ithc, &ithc->regs->dma_rx[0].status, + DMA_RX_STATUS_READY, DMA_RX_STATUS_READY); + writel(DMA_RX_STATUS_READY, &ithc->regs->dma_rx[0].status); + + u32 h = readl(&ithc->regs->input_header); + ithc_log_regs(ithc); + if (INPUT_HEADER_SYNC(h) != INPUT_HEADER_SYNC_VALUE + || INPUT_HEADER_VERSION(h) != INPUT_HEADER_VERSION_VALUE) { + pci_err(ithc->pci, "invalid input report frame header 0x%08x\n", h); + return -ENODATA; + } + return INPUT_HEADER_REPORT_LENGTH(h) * 4; +} + +static int ithc_quickspi_init_hidspi(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + pci_dbg(ithc->pci, "initializing HIDSPI\n"); + + // HIDSPI initialization sequence: + // "1. The host shall invoke the ACPI reset method to clear the device state." + acpi_status s = acpi_evaluate_object(ACPI_HANDLE(&ithc->pci->dev), "_RST", NULL, NULL); + if (ACPI_FAILURE(s)) { + pci_err(ithc->pci, "ACPI reset failed\n"); + return -EIO; + } + + bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); + + // "2. Within 1 second, the device shall signal an interrupt and make available to the host + // an input report containing a device reset response." + int size = wait_for_report(ithc); + if (size < 0) + return size; + if (size < sizeof(struct hidspi_header)) { + pci_err(ithc->pci, "SPI data size too small for reset response (%u)\n", size); + return -EMSGSIZE; + } + + // "3. The host shall read the reset response from the device at the Input Report addresses + // specified in ACPI." + u32 in_addr = cfg->has_input_report_body_address ? cfg->input_report_body_address : 0x1000; + struct { + struct hidspi_header header; + union { + struct hidspi_device_descriptor device_desc; + u32 data[16]; + }; + } resp = { 0 }; + if (size > sizeof(resp)) { + pci_err(ithc->pci, "SPI data size for reset response too big (%u)\n", size); + return -EMSGSIZE; + } + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp); + if (resp.header.type != HIDSPI_INPUT_TYPE_RESET_RESPONSE) { + pci_err(ithc->pci, "received type %i instead of reset response\n", resp.header.type); + return -ENOMSG; + } + + // "4. The host shall then write an Output Report to the device at the Output Report Address + // specified in ACPI, requesting the Device Descriptor from the device." + u32 out_addr = cfg->has_output_report_body_address ? cfg->output_report_body_address : 0x1000; + struct hidspi_header req = { .type = HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST }; + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_WRITE, out_addr, sizeof(req), &req); + + // "5. Within 1 second, the device shall signal an interrupt and make available to the host + // an input report containing the Device Descriptor." + size = wait_for_report(ithc); + if (size < 0) + return size; + if (size < sizeof(resp.header) + sizeof(resp.device_desc)) { + pci_err(ithc->pci, "SPI data size too small for device descriptor (%u)\n", size); + return -EMSGSIZE; + } + + // "6. The host shall read the Device Descriptor from the Input Report addresses specified + // in ACPI." + if (size > sizeof(resp)) { + pci_err(ithc->pci, "SPI data size for device descriptor too big (%u)\n", size); + return -EMSGSIZE; + } + memset(&resp, 0, sizeof(resp)); + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp); + if (resp.header.type != HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR) { + pci_err(ithc->pci, "received type %i instead of device descriptor\n", + resp.header.type); + return -ENOMSG; + } + struct hidspi_device_descriptor *d = &resp.device_desc; + if (resp.header.len < sizeof(*d)) { + pci_err(ithc->pci, "response too small for device descriptor (%u)\n", + resp.header.len); + return -EMSGSIZE; + } + if (d->wDeviceDescLength != sizeof(*d)) { + pci_err(ithc->pci, "invalid device descriptor length (%u)\n", + d->wDeviceDescLength); + return -EMSGSIZE; + } + + pci_info(ithc->pci, "Device descriptor: bcdVersion=0x%04x wReportDescLength=%u wMaxInputLength=%u wMaxOutputLength=%u wMaxFragmentLength=%u wVendorID=0x%04x wProductID=0x%04x wVersionID=0x%04x wFlags=0x%04x dwReserved=0x%08x\n", + d->bcdVersion, d->wReportDescLength, + d->wMaxInputLength, d->wMaxOutputLength, d->wMaxFragmentLength, + d->wVendorID, d->wProductID, d->wVersionID, + d->wFlags, d->dwReserved); + + ithc->vendor_id = d->wVendorID; + ithc->product_id = d->wProductID; + ithc->product_rev = d->wVersionID; + ithc->max_rx_size = max_t(u32, d->wMaxInputLength, + d->wReportDescLength + sizeof(struct hidspi_header)); + ithc->max_tx_size = d->wMaxOutputLength; + ithc->have_config = true; + + // "7. The device and host shall then enter their "Ready" states - where the device may + // begin sending Input Reports, and the device shall be prepared for Output Reports from + // the host." + + return 0; +} + +int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED); + + ithc_log_regs(ithc); + CHECK_RET(ithc_quickspi_init_regs, ithc, cfg); + ithc_log_regs(ithc); + CHECK_RET(ithc_quickspi_init_hidspi, ithc, cfg); + ithc_log_regs(ithc); + + // This value is set to 2 in ithc_quickspi_init_regs(). It needs to be set to 1 here, + // otherwise DMA will not work. Maybe selects between DMA and PIO mode? + bitsl(&ithc->regs->quickspi_config1, + QUICKSPI_CONFIG1_UNKNOWN_16(0xffff), QUICKSPI_CONFIG1_UNKNOWN_16(1)); + + // TODO Do we need to set any of the following bits here? + //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_4); + //bitsb_set(&ithc->regs->dma_rx[0].control2, DMA_RX_CONTROL2_UNKNOWN_5); + //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_5); + //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_3); + //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); + + ithc_log_regs(ithc); + + return 0; +} + +void ithc_quickspi_exit(struct ithc *ithc) +{ + // TODO Should we send HIDSPI 'power off' command? + //struct hidspi_header h = { .type = HIDSPI_OUTPUT_TYPE_COMMAND, .id = 3, }; + //struct ithc_data d = { .type = ITHC_DATA_RAW, .data = &h, .size = sizeof(h) }; + //CHECK(ithc_dma_tx, ithc, &d); // or ithc_spi_command() +} + +int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest) +{ + const struct hidspi_header *hdr = src; + + if (len < sizeof(*hdr)) + return -ENODATA; + // TODO Do we need to handle HIDSPI packet fragmentation? + if (len < sizeof(*hdr) + hdr->len) + return -EMSGSIZE; + if (len > round_up(sizeof(*hdr) + hdr->len, 4)) + return -EMSGSIZE; + + switch (hdr->type) { + case HIDSPI_INPUT_TYPE_RESET_RESPONSE: + // TODO "When the device detects an error condition, it may interrupt and make + // available to the host an Input Report containing an unsolicited Reset Response. + // After receiving an unsolicited Reset Response, the host shall initiate the + // request procedure from step (4) in the [HIDSPI initialization] process." + dest->type = ITHC_DATA_ERROR; + return 0; + case HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR: + dest->type = ITHC_DATA_REPORT_DESCRIPTOR; + dest->data = hdr + 1; + dest->size = hdr->len; + return 0; + case HIDSPI_INPUT_TYPE_DATA: + case HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE: + dest->type = ITHC_DATA_INPUT_REPORT; + dest->data = &hdr->id; + dest->size = hdr->len + 1; + return 0; + case HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE: + dest->type = ITHC_DATA_GET_FEATURE; + dest->data = &hdr->id; + dest->size = hdr->len + 1; + return 0; + case HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE: + case HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE: + dest->type = ITHC_DATA_IGNORE; + return 0; + default: + return -EINVAL; + } +} + +ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen) +{ + struct hidspi_header *hdr = dest; + + size_t src_size = src->size; + const u8 *src_data = src->data; + u8 type; + + switch (src->type) { + case ITHC_DATA_SET_FEATURE: + type = HIDSPI_OUTPUT_TYPE_SET_FEATURE; + break; + case ITHC_DATA_GET_FEATURE: + type = HIDSPI_OUTPUT_TYPE_GET_FEATURE; + break; + case ITHC_DATA_OUTPUT_REPORT: + type = HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT; + break; + case ITHC_DATA_REPORT_DESCRIPTOR: + type = HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST; + src_size = 0; + break; + default: + return -EINVAL; + } + + u8 id = 0; + if (src_size) { + id = *src_data++; + src_size--; + } + + // Data must be padded to next 4-byte boundary. + size_t padded = round_up(src_size, 4); + if (sizeof(*hdr) + padded > maxlen) + return -EOVERFLOW; + + // Fill the TX buffer with header and data. + hdr->type = type; + hdr->len = (u16)src_size; + hdr->id = id; + memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0); + + return sizeof(*hdr) + padded; +} + diff --git a/drivers/hid/ithc/ithc-quickspi.h b/drivers/hid/ithc/ithc-quickspi.h new file mode 100644 index 00000000000000..74d882f6b2f0a7 --- /dev/null +++ b/drivers/hid/ithc/ithc-quickspi.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +struct ithc_acpi_config { + bool has_config: 1; + bool has_input_report_header_address: 1; + bool has_input_report_body_address: 1; + bool has_output_report_body_address: 1; + bool has_read_opcode: 1; + bool has_write_opcode: 1; + bool has_read_mode: 1; + bool has_write_mode: 1; + bool has_spi_frequency: 1; + bool has_limit_packet_size: 1; + bool has_tx_delay: 1; + bool has_active_ltr: 1; + bool has_idle_ltr: 1; + u32 input_report_header_address; + u32 input_report_body_address; + u32 output_report_body_address; + u8 read_opcode; + u8 write_opcode; + u8 read_mode; + u8 write_mode; + u32 spi_frequency; + u32 limit_packet_size; + u32 tx_delay; // us/10 // TODO use? + u32 active_ltr; // ns/1024 + u32 idle_ltr; // ns/1024 +}; + +int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg); +void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg); + +int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg); +void ithc_quickspi_exit(struct ithc *ithc); +int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest); +ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen); + diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c new file mode 100644 index 00000000000000..c0f13506af205e --- /dev/null +++ b/drivers/hid/ithc/ithc-regs.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +#define reg_num(r) (0x1fff & (u16)(__force u64)(r)) + +void bitsl(__iomem u32 *reg, u32 mask, u32 val) +{ + if (val & ~mask) + pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", + reg_num(reg), val, mask); + writel((readl(reg) & ~mask) | (val & mask), reg); +} + +void bitsb(__iomem u8 *reg, u8 mask, u8 val) +{ + if (val & ~mask) + pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", + reg_num(reg), val, mask); + writeb((readb(reg) & ~mask) | (val & mask), reg); +} + +int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) +{ + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", + reg_num(reg), mask, val); + u32 x; + if (readl_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { + ithc_log_regs(ithc); + pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", + reg_num(reg), mask, val); + return -ETIMEDOUT; + } + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "done waiting\n"); + return 0; +} + +int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) +{ + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", + reg_num(reg), mask, val); + u8 x; + if (readb_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { + ithc_log_regs(ithc); + pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", + reg_num(reg), mask, val); + return -ETIMEDOUT; + } + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "done waiting\n"); + return 0; +} + +static void calc_ltr(u64 *ns, unsigned int *val, unsigned int *scale) +{ + unsigned int s = 0; + u64 v = *ns; + while (v > 0x3ff) { + s++; + v >>= 5; + } + if (s > 5) { + s = 5; + v = 0x3ff; + } + *val = v; + *scale = s; + *ns = v << (5 * s); +} + +void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns) +{ + unsigned int active_val, active_scale, idle_val, idle_scale; + calc_ltr(&active_ltr_ns, &active_val, &active_scale); + calc_ltr(&idle_ltr_ns, &idle_val, &idle_scale); + pci_dbg(ithc->pci, "setting active LTR value to %llu ns, idle LTR value to %llu ns\n", + active_ltr_ns, idle_ltr_ns); + writel(LTR_CONFIG_ENABLE_ACTIVE | LTR_CONFIG_ENABLE_IDLE | LTR_CONFIG_APPLY | + LTR_CONFIG_ACTIVE_LTR_SCALE(active_scale) | LTR_CONFIG_ACTIVE_LTR_VALUE(active_val) | + LTR_CONFIG_IDLE_LTR_SCALE(idle_scale) | LTR_CONFIG_IDLE_LTR_VALUE(idle_val), + &ithc->regs->ltr_config); +} + +void ithc_set_ltr_idle(struct ithc *ithc) +{ + u32 ltr = readl(&ithc->regs->ltr_config); + switch (ltr & (LTR_CONFIG_STATUS_ACTIVE | LTR_CONFIG_STATUS_IDLE)) { + case LTR_CONFIG_STATUS_IDLE: + break; + case LTR_CONFIG_STATUS_ACTIVE: + writel(ltr | LTR_CONFIG_TOGGLE | LTR_CONFIG_APPLY, &ithc->regs->ltr_config); + break; + default: + pci_err(ithc->pci, "invalid LTR state 0x%08x\n", ltr); + break; + } +} + +int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_mode) +{ + if (clkdiv == 0 || clkdiv > 7 || read_mode > SPI_MODE_QUAD || write_mode > SPI_MODE_QUAD) + return -EINVAL; + static const char * const modes[] = { "single", "dual", "quad" }; + pci_dbg(ithc->pci, "setting SPI frequency to %i Hz, %s read, %s write\n", + SPI_CLK_FREQ_BASE / (clkdiv * (clkdiv8 ? 8 : 1)), + modes[read_mode], modes[write_mode]); + bitsl(&ithc->regs->spi_config, + SPI_CONFIG_READ_MODE(0xff) | SPI_CONFIG_READ_CLKDIV(0xff) | + SPI_CONFIG_WRITE_MODE(0xff) | SPI_CONFIG_WRITE_CLKDIV(0xff) | + SPI_CONFIG_CLKDIV_8, + SPI_CONFIG_READ_MODE(read_mode) | SPI_CONFIG_READ_CLKDIV(clkdiv) | + SPI_CONFIG_WRITE_MODE(write_mode) | SPI_CONFIG_WRITE_CLKDIV(clkdiv) | + (clkdiv8 ? SPI_CONFIG_CLKDIV_8 : 0)); + return 0; +} + +int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) +{ + pci_dbg(ithc->pci, "SPI command %u, size %u, offset 0x%x\n", command, size, offset); + if (size > sizeof(ithc->regs->spi_cmd.data)) + return -EINVAL; + + // Wait if the device is still busy. + CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0); + // Clear result flags. + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + + // Init SPI command data. + writeb(command, &ithc->regs->spi_cmd.code); + writew(size, &ithc->regs->spi_cmd.size); + writel(offset, &ithc->regs->spi_cmd.offset); + u32 *p = data, n = (size + 3) / 4; + for (u32 i = 0; i < n; i++) + writel(p[i], &ithc->regs->spi_cmd.data[i]); + + // Start transmission. + bitsb_set(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND); + CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0); + + // Read response. + if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE) + return -EIO; + if (readw(&ithc->regs->spi_cmd.size) != size) + return -EMSGSIZE; + for (u32 i = 0; i < n; i++) + p[i] = readl(&ithc->regs->spi_cmd.data[i]); + + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + return 0; +} + diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h new file mode 100644 index 00000000000000..4f541fe533facf --- /dev/null +++ b/drivers/hid/ithc/ithc-regs.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +#define LTR_CONFIG_ENABLE_ACTIVE BIT(0) +#define LTR_CONFIG_TOGGLE BIT(1) +#define LTR_CONFIG_ENABLE_IDLE BIT(2) +#define LTR_CONFIG_APPLY BIT(3) +#define LTR_CONFIG_IDLE_LTR_SCALE(x) (((x) & 7) << 4) +#define LTR_CONFIG_IDLE_LTR_VALUE(x) (((x) & 0x3ff) << 7) +#define LTR_CONFIG_ACTIVE_LTR_SCALE(x) (((x) & 7) << 17) +#define LTR_CONFIG_ACTIVE_LTR_VALUE(x) (((x) & 0x3ff) << 20) +#define LTR_CONFIG_STATUS_ACTIVE BIT(30) +#define LTR_CONFIG_STATUS_IDLE BIT(31) + +#define CONTROL_QUIESCE BIT(1) +#define CONTROL_IS_QUIESCED BIT(2) +#define CONTROL_NRESET BIT(3) +#define CONTROL_UNKNOWN_24(x) (((x) & 3) << 24) +#define CONTROL_READY BIT(29) + +#define SPI_CONFIG_READ_MODE(x) (((x) & 3) << 2) +#define SPI_CONFIG_READ_CLKDIV(x) (((x) & 7) << 4) +#define SPI_CONFIG_READ_PACKET_SIZE(x) (((x) & 0x1ff) << 7) +#define SPI_CONFIG_WRITE_MODE(x) (((x) & 3) << 18) +#define SPI_CONFIG_WRITE_CLKDIV(x) (((x) & 7) << 20) +#define SPI_CONFIG_CLKDIV_8 BIT(23) // additionally divide clk by 8, for both read and write +#define SPI_CONFIG_WRITE_PACKET_SIZE(x) (((x) & 0xff) << 24) + +#define SPI_CLK_FREQ_BASE 125000000 +#define SPI_MODE_SINGLE 0 +#define SPI_MODE_DUAL 1 +#define SPI_MODE_QUAD 2 + +#define ERROR_CONTROL_UNKNOWN_0 BIT(0) +#define ERROR_CONTROL_DISABLE_DMA BIT(1) // clears DMA_RX_CONTROL_ENABLE when a DMA error occurs +#define ERROR_CONTROL_UNKNOWN_2 BIT(2) +#define ERROR_CONTROL_UNKNOWN_3 BIT(3) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_9 BIT(9) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_10 BIT(10) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_12 BIT(12) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_13 BIT(13) +#define ERROR_CONTROL_UNKNOWN_16(x) (((x) & 0xff) << 16) // spi error code irq? +#define ERROR_CONTROL_SET_DMA_STATUS BIT(29) // sets DMA_RX_STATUS_ERROR when a DMA error occurs + +#define ERROR_STATUS_DMA BIT(28) +#define ERROR_STATUS_SPI BIT(30) + +#define ERROR_FLAG_DMA_UNKNOWN_9 BIT(9) +#define ERROR_FLAG_DMA_UNKNOWN_10 BIT(10) +#define ERROR_FLAG_DMA_RX_TIMEOUT BIT(12) // set when we receive a truncated DMA message +#define ERROR_FLAG_DMA_UNKNOWN_13 BIT(13) +#define ERROR_FLAG_SPI_BUS_TURNAROUND BIT(16) +#define ERROR_FLAG_SPI_RESPONSE_TIMEOUT BIT(17) +#define ERROR_FLAG_SPI_INTRA_PACKET_TIMEOUT BIT(18) +#define ERROR_FLAG_SPI_INVALID_RESPONSE BIT(19) +#define ERROR_FLAG_SPI_HS_RX_TIMEOUT BIT(20) +#define ERROR_FLAG_SPI_TOUCH_IC_INIT BIT(21) + +#define SPI_CMD_CONTROL_SEND BIT(0) // cleared by device when sending is complete +#define SPI_CMD_CONTROL_IRQ BIT(1) + +#define SPI_CMD_CODE_READ 4 +#define SPI_CMD_CODE_WRITE 6 + +#define SPI_CMD_STATUS_DONE BIT(0) +#define SPI_CMD_STATUS_ERROR BIT(1) +#define SPI_CMD_STATUS_BUSY BIT(3) + +#define DMA_TX_CONTROL_SEND BIT(0) // cleared by device when sending is complete +#define DMA_TX_CONTROL_IRQ BIT(3) + +#define DMA_TX_STATUS_DONE BIT(0) +#define DMA_TX_STATUS_ERROR BIT(1) +#define DMA_TX_STATUS_UNKNOWN_2 BIT(2) +#define DMA_TX_STATUS_UNKNOWN_3 BIT(3) // busy? + +#define INPUT_HEADER_VERSION(x) ((x) & 0xf) +#define INPUT_HEADER_REPORT_LENGTH(x) (((x) >> 8) & 0x3fff) +#define INPUT_HEADER_SYNC(x) ((x) >> 24) +#define INPUT_HEADER_VERSION_VALUE 3 +#define INPUT_HEADER_SYNC_VALUE 0x5a + +#define QUICKSPI_CONFIG1_UNKNOWN_0(x) (((x) & 0x1f) << 0) +#define QUICKSPI_CONFIG1_UNKNOWN_5(x) (((x) & 0x1f) << 5) +#define QUICKSPI_CONFIG1_UNKNOWN_10(x) (((x) & 0x1f) << 10) +#define QUICKSPI_CONFIG1_UNKNOWN_16(x) (((x) & 0xffff) << 16) + +#define QUICKSPI_CONFIG2_UNKNOWN_0(x) (((x) & 0x1f) << 0) +#define QUICKSPI_CONFIG2_UNKNOWN_5(x) (((x) & 0x1f) << 5) +#define QUICKSPI_CONFIG2_UNKNOWN_12(x) (((x) & 0xf) << 12) +#define QUICKSPI_CONFIG2_UNKNOWN_16 BIT(16) +#define QUICKSPI_CONFIG2_UNKNOWN_17 BIT(17) +#define QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT BIT(24) +#define QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT BIT(25) +#define QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE BIT(27) +#define QUICKSPI_CONFIG2_IRQ_POLARITY BIT(28) + +#define DMA_RX_CONTROL_ENABLE BIT(0) +#define DMA_RX_CONTROL_IRQ_UNKNOWN_1 BIT(1) // rx1 only? +#define DMA_RX_CONTROL_IRQ_ERROR BIT(3) // rx1 only? +#define DMA_RX_CONTROL_IRQ_READY BIT(4) // rx0 only +#define DMA_RX_CONTROL_IRQ_DATA BIT(5) + +#define DMA_RX_CONTROL2_UNKNOWN_4 BIT(4) // rx1 only? +#define DMA_RX_CONTROL2_UNKNOWN_5 BIT(5) // rx0 only? +#define DMA_RX_CONTROL2_RESET BIT(7) // resets ringbuffer indices + +#define DMA_RX_WRAP_FLAG BIT(7) + +#define DMA_RX_STATUS_ERROR BIT(3) +#define DMA_RX_STATUS_READY BIT(4) // set in rx0 after using CONTROL_NRESET when it becomes possible to read config (can take >100ms) +#define DMA_RX_STATUS_HAVE_DATA BIT(5) +#define DMA_RX_STATUS_ENABLED BIT(8) + +#define INIT_UNKNOWN_GUC_2 BIT(2) +#define INIT_UNKNOWN_3 BIT(3) +#define INIT_UNKNOWN_GUC_4 BIT(4) +#define INIT_UNKNOWN_5 BIT(5) +#define INIT_UNKNOWN_31 BIT(31) + +// COUNTER_RESET can be written to counter registers to reset them to zero. However, in some cases this can mess up the THC. +#define COUNTER_RESET BIT(31) + +struct ithc_registers { + /* 0000 */ u32 _unknown_0000[5]; + /* 0014 */ u32 ltr_config; + /* 0018 */ u32 _unknown_0018[1018]; + /* 1000 */ u32 _unknown_1000; + /* 1004 */ u32 _unknown_1004; + /* 1008 */ u32 control_bits; + /* 100c */ u32 _unknown_100c; + /* 1010 */ u32 spi_config; + struct { + /* 1014/1018/101c */ u8 header; + /* 1015/1019/101d */ u8 quad; + /* 1016/101a/101e */ u8 dual; + /* 1017/101b/101f */ u8 single; + } opcode[3]; + /* 1020 */ u32 error_control; + /* 1024 */ u32 error_status; // write to clear + /* 1028 */ u32 error_flags; // write to clear + /* 102c */ u32 _unknown_102c[5]; + struct { + /* 1040 */ u8 control; + /* 1041 */ u8 code; + /* 1042 */ u16 size; + /* 1044 */ u32 status; // write to clear + /* 1048 */ u32 offset; + /* 104c */ u32 data[16]; + /* 108c */ u32 _unknown_108c; + } spi_cmd; + struct { + /* 1090 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq() + /* 1098 */ u8 control; + /* 1099 */ u8 _unknown_1099; + /* 109a */ u8 _unknown_109a; + /* 109b */ u8 num_prds; + /* 109c */ u32 status; // write to clear + /* 10a0 */ u32 _unknown_10a0[5]; + /* 10b4 */ u32 spi_addr; + } dma_tx; + /* 10b8 */ u32 spi_header_addr; + union { + /* 10bc */ u32 irq_cause; // in legacy THC mode + /* 10bc */ u32 input_header; // in QuickSPI mode (see HIDSPI spec) + }; + /* 10c0 */ u32 _unknown_10c0[8]; + /* 10e0 */ u32 _unknown_10e0_counters[3]; + /* 10ec */ u32 quickspi_config1; + /* 10f0 */ u32 quickspi_config2; + /* 10f4 */ u32 _unknown_10f4[3]; + struct { + /* 1100/1200 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq() + /* 1108/1208 */ u8 num_bufs; + /* 1109/1209 */ u8 num_prds; + /* 110a/120a */ u16 _unknown_110a; + /* 110c/120c */ u8 control; + /* 110d/120d */ u8 head; + /* 110e/120e */ u8 tail; + /* 110f/120f */ u8 control2; + /* 1110/1210 */ u32 status; // write to clear + /* 1114/1214 */ u32 _unknown_1114; + /* 1118/1218 */ u64 _unknown_1118_guc_addr; + /* 1120/1220 */ u32 _unknown_1120_guc; + /* 1124/1224 */ u32 _unknown_1124_guc; + /* 1128/1228 */ u32 init_unknown; + /* 112c/122c */ u32 _unknown_112c; + /* 1130/1230 */ u64 _unknown_1130_guc_addr; + /* 1138/1238 */ u32 _unknown_1138_guc; + /* 113c/123c */ u32 _unknown_113c; + /* 1140/1240 */ u32 _unknown_1140_guc; + /* 1144/1244 */ u32 _unknown_1144[11]; + /* 1170/1270 */ u32 spi_addr; + /* 1174/1274 */ u32 _unknown_1174[11]; + /* 11a0/12a0 */ u32 _unknown_11a0_counters[6]; + /* 11b8/12b8 */ u32 _unknown_11b8[18]; + } dma_rx[2]; +}; +static_assert(sizeof(struct ithc_registers) == 0x1300); + +void bitsl(__iomem u32 *reg, u32 mask, u32 val); +void bitsb(__iomem u8 *reg, u8 mask, u8 val); +#define bitsl_set(reg, x) bitsl(reg, x, x) +#define bitsb_set(reg, x) bitsb(reg, x, x) +int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val); +int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val); + +void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns); +void ithc_set_ltr_idle(struct ithc *ithc); +int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_mode); +int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data); + diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h new file mode 100644 index 00000000000000..aec320d4e945c0 --- /dev/null +++ b/drivers/hid/ithc/ithc.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVNAME "ithc" +#define DEVFULLNAME "Intel Touch Host Controller" + +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define CHECK(fn, ...) ({ int r = fn(__VA_ARGS__); if (r < 0) pci_err(ithc->pci, "%s: %s failed with %i\n", __func__, #fn, r); r; }) +#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while (0) + +#define NUM_RX_BUF 16 + +// PCI device IDs: +// Lakefield +#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT1 0x98d0 +#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT2 0x98d1 +// Tiger Lake +#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1 0xa0d0 +#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2 0xa0d1 +#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1 0x43d0 +#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2 0x43d1 +// Alder Lake +#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1 0x7ad8 +#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2 0x7ad9 +#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1 0x51d0 +#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2 0x51d1 +#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1 0x54d0 +#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2 0x54d1 +// Raptor Lake +#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1 0x7a58 +#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2 0x7a59 +// Meteor Lake +#define PCI_DEVICE_ID_INTEL_THC_MTL_S_PORT1 0x7f59 +#define PCI_DEVICE_ID_INTEL_THC_MTL_S_PORT2 0x7f5b +#define PCI_DEVICE_ID_INTEL_THC_MTL_MP_PORT1 0x7e49 +#define PCI_DEVICE_ID_INTEL_THC_MTL_MP_PORT2 0x7e4b + +struct ithc; + +#include "ithc-regs.h" +#include "ithc-hid.h" +#include "ithc-dma.h" +#include "ithc-legacy.h" +#include "ithc-quickspi.h" +#include "ithc-debug.h" + +struct ithc { + char phys[32]; + struct pci_dev *pci; + int irq; + struct task_struct *poll_thread; + struct timer_list idle_timer; + + struct ithc_registers __iomem *regs; + struct ithc_registers *prev_regs; // for debugging + struct ithc_dma_rx dma_rx[2]; + struct ithc_dma_tx dma_tx; + struct ithc_hid hid; + + bool use_quickspi; + bool have_config; + u16 vendor_id; + u16 product_id; + u32 product_rev; + u32 max_rx_size; + u32 max_tx_size; + u32 legacy_touch_cfg; +}; + +int ithc_reset(struct ithc *ithc); + From c32700d7aec0a05ce3cc7aa9721210bf6aace8d8 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 17 Jun 2022 02:14:00 +0200 Subject: [PATCH 408/521] SURFACE: rtc: Add basic support for RTC via Surface System Aggregator Module Signed-off-by: Maximilian Luz Patchset: surface-sam Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/rtc/Kconfig | 7 +++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-surface.c | 129 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 drivers/rtc/rtc-surface.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index af420a5f9585a2..8676b35b63aa66 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1425,6 +1425,13 @@ config RTC_DRV_NTXEC embedded controller found in certain e-book readers designed by the original design manufacturer Netronix. +config RTC_DRV_SURFACE + tristate "Microsoft Surface Aggregator RTC" + depends on SURFACE_AGGREGATOR + depends on SURFACE_AGGREGATOR_BUS + help + TODO + comment "on-CPU RTC drivers" config RTC_DRV_ASM9260 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6cf7e066314e13..18bd33b84d1399 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -186,6 +186,7 @@ obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o obj-$(CONFIG_RTC_DRV_SUNPLUS) += rtc-sunplus.o obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o +obj-$(CONFIG_RTC_DRV_SURFACE) += rtc-surface.o obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TI_K3) += rtc-ti-k3.o diff --git a/drivers/rtc/rtc-surface.c b/drivers/rtc/rtc-surface.c new file mode 100644 index 00000000000000..f6c17c4e98d5f1 --- /dev/null +++ b/drivers/rtc/rtc-surface.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AC driver for 7th-generation Microsoft Surface devices via Surface System + * Aggregator Module (SSAM). + * + * Copyright (C) 2019-2021 Maximilian Luz + */ + +#include +#include +#include +#include +#include + +#include + +struct surface_rtc { + struct ssam_device *sdev; + struct rtc_device *rtc; +}; + +SSAM_DEFINE_SYNC_REQUEST_R(__ssam_rtc_get_unix_time, __le32, { + .target_category = SSAM_SSH_TC_SAM, + .target_id = SSAM_SSH_TID_SAM, + .instance_id = 0x00, + .command_id = 0x10, +}); + +SSAM_DEFINE_SYNC_REQUEST_W(__ssam_rtc_set_unix_time, __le32, { + .target_category = SSAM_SSH_TC_SAM, + .target_id = SSAM_SSH_TID_SAM, + .instance_id = 0x00, + .command_id = 0x0f, +}); + +static int ssam_rtc_get_unix_time(struct surface_rtc *srtc, u32 *time) +{ + __le32 time_le; + int status; + + status = __ssam_rtc_get_unix_time(srtc->sdev->ctrl, &time_le); + if (status) + return status; + + *time = le32_to_cpu(time_le); + return 0; +} + +static int ssam_rtc_set_unix_time(struct surface_rtc *srtc, u32 time) +{ + __le32 time_le = cpu_to_le32(time); + + return __ssam_rtc_set_unix_time(srtc->sdev->ctrl, &time_le); +} + +static int surface_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct surface_rtc *srtc = dev_get_drvdata(dev); + int status; + u32 time; + + status = ssam_rtc_get_unix_time(srtc, &time); + if (status) + return status; + + rtc_time64_to_tm(time, tm); + return 0; +} + +static int surface_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct surface_rtc *srtc = dev_get_drvdata(dev); + time64_t time = rtc_tm_to_time64(tm); + + return ssam_rtc_set_unix_time(srtc, (u32)time); +} + +static const struct rtc_class_ops surface_rtc_ops = { + .read_time = surface_rtc_read_time, + .set_time = surface_rtc_set_time, +}; + +static int surface_rtc_probe(struct ssam_device *sdev) +{ + struct surface_rtc *srtc; + + srtc = devm_kzalloc(&sdev->dev, sizeof(*srtc), GFP_KERNEL); + if (!srtc) + return -ENOMEM; + + srtc->sdev = sdev; + + srtc->rtc = devm_rtc_allocate_device(&sdev->dev); + if (IS_ERR(srtc->rtc)) + return PTR_ERR(srtc->rtc); + + srtc->rtc->ops = &surface_rtc_ops; + srtc->rtc->range_max = U32_MAX; + + ssam_device_set_drvdata(sdev, srtc); + + return devm_rtc_register_device(srtc->rtc); +} + +static void surface_rtc_remove(struct ssam_device *sdev) +{ + /* Device-managed allocations take care of everything... */ +} + +static const struct ssam_device_id surface_rtc_match[] = { + { SSAM_SDEV(SAM, SAM, 0x00, 0x00) }, + { }, +}; +MODULE_DEVICE_TABLE(ssam, surface_rtc_match); + +static struct ssam_device_driver surface_rtc_driver = { + .probe = surface_rtc_probe, + .remove = surface_rtc_remove, + .match_table = surface_rtc_match, + .driver = { + .name = "surface_rtc", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_ssam_device_driver(surface_rtc_driver); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("RTC driver for Surface System Aggregator Module"); +MODULE_LICENSE("GPL"); From 868226483ff09cc685455c490bc900a599ba2dbd Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 20 Apr 2025 01:05:14 +0200 Subject: [PATCH 409/521] SURFACE: platform/surface: aggregator_registry: Add Surface Laptop 7 (ACPI) Patchset: surface-sam Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/surface_aggregator_registry.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index f0881edfb61610..c935d2518615f6 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -474,6 +474,9 @@ static const struct acpi_device_id ssam_platform_hub_acpi_match[] = { /* Surface Laptop 6 */ { "MSHW0530", (unsigned long)ssam_node_group_sl6 }, + /* Surface Laptop 7 */ + { "MSHW0551", (unsigned long)ssam_node_group_sl7 }, + /* Surface Laptop Go 1 */ { "MSHW0118", (unsigned long)ssam_node_group_slg1 }, From 34cc1e83388e06a324ed23b61e7ce6b80e6e3a52 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH 410/521] SURFACE: i2c: acpi: Implement RawBytes read access Microsoft Surface Pro 4 and Book 1 devices access the MSHW0030 I2C device via a generic serial bus operation region and RawBytes read access. On the Surface Book 1, this access is required to turn on (and off) the discrete GPU. Multiple things are to note here: a) The RawBytes access is device/driver dependent. The ACPI specification states: > Raw accesses assume that the writer has knowledge of the bus that > the access is made over and the device that is being accessed. The > protocol may only ensure that the buffer is transmitted to the > appropriate driver, but the driver must be able to interpret the > buffer to communicate to a register. Thus this implementation may likely not work on other devices accessing I2C via the RawBytes accessor type. b) The MSHW0030 I2C device is an HID-over-I2C device which seems to serve multiple functions: 1. It is the main access point for the legacy-type Surface Aggregator Module (also referred to as SAM-over-HID, as opposed to the newer SAM-over-SSH/UART). It has currently not been determined on how support for the legacy SAM should be implemented. Likely via a custom HID driver. 2. It seems to serve as the HID device for the Integrated Sensor Hub. This might complicate matters with regards to implementing a SAM-over-HID driver required by legacy SAM. In light of this, the simplest approach has been chosen for now. However, it may make more sense regarding breakage and compatibility to either provide functionality for replacing or enhancing the default operation region handler via some additional API functions, or even to completely blacklist MSHW0030 from the I2C core and provide a custom driver for it. Replacing/enhancing the default operation region handler would, however, either require some sort of secondary driver and access point for it, from which the new API functions would be called and the new handler (part) would be installed, or hard-coding them via some sort of quirk-like interface into the I2C core. Signed-off-by: Maximilian Luz Patchset: surface-sam-over-hid Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/i2c/i2c-core-acpi.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 28c0e4884a7f2e..26ae925ab5d60c 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -663,6 +663,27 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, return (ret == 1) ? 0 : -EIO; } +static int acpi_gsb_i2c_write_raw_bytes(struct i2c_client *client, + u8 *data, u8 data_len) +{ + struct i2c_msg msgs[1]; + int ret; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = data_len + 1; + msgs[0].buf = data; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); + return ret; + } + + /* 1 transfer must have completed successfully */ + return (ret == 1) ? 0 : -EIO; +} + static acpi_status i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, @@ -764,6 +785,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, } break; + case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES: + if (action == ACPI_READ) { + dev_warn(&adapter->dev, + "protocol 0x%02x not supported for client 0x%02x\n", + accessor_type, client->addr); + ret = AE_BAD_PARAMETER; + goto err; + } else { + status = acpi_gsb_i2c_write_raw_bytes(client, + gsb->data, info->access_length); + } + break; + default: dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", accessor_type, client->addr); From e8c8cbd15811ac6746ca5407a3f7b10403b0721d Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 13 Feb 2021 16:41:18 +0100 Subject: [PATCH 411/521] SURFACE: platform/surface: Add driver for Surface Book 1 dGPU switch Add driver exposing the discrete GPU power-switch of the Microsoft Surface Book 1 to user-space. On the Surface Book 1, the dGPU power is controlled via the Surface System Aggregator Module (SAM). The specific SAM-over-HID command for this is exposed via ACPI. This module provides a simple driver exposing the ACPI call via a sysfs parameter to user-space, so that users can easily power-on/-off the dGPU. Patchset: surface-sam-over-hid Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/Kconfig | 7 + drivers/platform/surface/Makefile | 1 + .../surface/surfacebook1_dgpu_switch.c | 136 ++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 drivers/platform/surface/surfacebook1_dgpu_switch.c diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig index f775c6ca1ec1dd..2075e3852053a8 100644 --- a/drivers/platform/surface/Kconfig +++ b/drivers/platform/surface/Kconfig @@ -149,6 +149,13 @@ config SURFACE_AGGREGATOR_TABLET_SWITCH Select M or Y here, if you want to provide tablet-mode switch input events on the Surface Pro 8, Surface Pro X, and Surface Laptop Studio. +config SURFACE_BOOK1_DGPU_SWITCH + tristate "Surface Book 1 dGPU Switch Driver" + depends on SYSFS + help + This driver provides a sysfs switch to set the power-state of the + discrete GPU found on the Microsoft Surface Book 1. + config SURFACE_DTX tristate "Surface DTX (Detachment System) Driver" depends on SURFACE_AGGREGATOR diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile index 53344330939bf0..7efcd0cdb5329f 100644 --- a/drivers/platform/surface/Makefile +++ b/drivers/platform/surface/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o obj-$(CONFIG_SURFACE_AGGREGATOR_HUB) += surface_aggregator_hub.o obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o +obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += surfacebook1_dgpu_switch.o obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o diff --git a/drivers/platform/surface/surfacebook1_dgpu_switch.c b/drivers/platform/surface/surfacebook1_dgpu_switch.c new file mode 100644 index 00000000000000..68db237734a13e --- /dev/null +++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +/* MSHW0040/VGBI DSM UUID: 6fd05c69-cde3-49f4-95ed-ab1665498035 */ +static const guid_t dgpu_sw_guid = + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, + 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35); + +#define DGPUSW_ACPI_PATH_DSM "\\_SB_.PCI0.LPCB.EC0_.VGBI" +#define DGPUSW_ACPI_PATH_HGON "\\_SB_.PCI0.RP05.HGON" +#define DGPUSW_ACPI_PATH_HGOF "\\_SB_.PCI0.RP05.HGOF" + +static int sb1_dgpu_sw_dsmcall(void) +{ + union acpi_object *obj; + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle); + if (status) + return -EINVAL; + + obj = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER); + if (!obj) + return -EINVAL; + + ACPI_FREE(obj); + return 0; +} + +static int sb1_dgpu_sw_hgon(struct device *dev) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf); + if (status) { + dev_err(dev, "failed to run HGON: %d\n", status); + return -EINVAL; + } + + ACPI_FREE(buf.pointer); + + dev_info(dev, "turned-on dGPU via HGON\n"); + return 0; +} + +static int sb1_dgpu_sw_hgof(struct device *dev) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf); + if (status) { + dev_err(dev, "failed to run HGOF: %d\n", status); + return -EINVAL; + } + + ACPI_FREE(buf.pointer); + + dev_info(dev, "turned-off dGPU via HGOF\n"); + return 0; +} + +static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + bool value; + int status; + + status = kstrtobool(buf, &value); + if (status < 0) + return status; + + if (!value) + return 0; + + status = sb1_dgpu_sw_dsmcall(); + + return status < 0 ? status : len; +} +static DEVICE_ATTR_WO(dgpu_dsmcall); + +static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + bool power; + int status; + + status = kstrtobool(buf, &power); + if (status < 0) + return status; + + if (power) + status = sb1_dgpu_sw_hgon(dev); + else + status = sb1_dgpu_sw_hgof(dev); + + return status < 0 ? status : len; +} +static DEVICE_ATTR_WO(dgpu_power); + +static struct attribute *sb1_dgpu_sw_attrs[] = { + &dev_attr_dgpu_dsmcall.attr, + &dev_attr_dgpu_power.attr, + NULL +}; +ATTRIBUTE_GROUPS(sb1_dgpu_sw); + +/* + * The dGPU power seems to be actually handled by MSHW0040. However, that is + * also the power-/volume-button device with a mainline driver. So let's use + * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device. + */ +static const struct acpi_device_id sb1_dgpu_sw_match[] = { + { "MSHW0041", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match); + +static struct platform_driver sb1_dgpu_sw = { + .driver = { + .name = "surfacebook1_dgpu_switch", + .acpi_match_table = sb1_dgpu_sw_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .dev_groups = sb1_dgpu_sw_groups, + }, +}; +module_platform_driver(sb1_dgpu_sw); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1"); +MODULE_LICENSE("GPL"); From c2f681224bca50c47ed2aec393d79685ac165373 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:05:09 +1100 Subject: [PATCH 412/521] SURFACE: Input: soc_button_array - support AMD variant Surface devices The power button on the AMD variant of the Surface Laptop uses the same MSHW0040 device ID as the 5th and later generation of Surface devices, however they report 0 for their OEM platform revision. As the _DSM does not exist on the devices requiring special casing, check for the existance of the _DSM to determine if soc_button_array should be loaded. Fixes: c394159310d0 ("Input: soc_button_array - add support for newer surface devices") Co-developed-by: Maximilian Luz Signed-off-by: Sachi King Patchset: surface-button Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/input/misc/soc_button_array.c | 33 +++++++-------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index b8cad415c62ca2..43b5d56383e36c 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -540,8 +540,8 @@ static const struct soc_device_data soc_device_MSHW0028 = { * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned * devices use MSHW0040 for power and volume buttons, however the way they * have to be addressed differs. Make sure that we only load this drivers - * for the correct devices by checking the OEM Platform Revision provided by - * the _DSM method. + * for the correct devices by checking if the OEM Platform Revision DSM call + * exists. */ #define MSHW0040_DSM_REVISION 0x01 #define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision @@ -552,31 +552,14 @@ static const guid_t MSHW0040_DSM_UUID = static int soc_device_check_MSHW0040(struct device *dev) { acpi_handle handle = ACPI_HANDLE(dev); - union acpi_object *result; - u64 oem_platform_rev = 0; // valid revisions are nonzero - - // get OEM platform revision - result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, - MSHW0040_DSM_REVISION, - MSHW0040_DSM_GET_OMPR, NULL, - ACPI_TYPE_INTEGER); - - if (result) { - oem_platform_rev = result->integer.value; - ACPI_FREE(result); - } - - /* - * If the revision is zero here, the _DSM evaluation has failed. This - * indicates that we have a Pro 4 or Book 1 and this driver should not - * be used. - */ - if (oem_platform_rev == 0) - return -ENODEV; + bool exists; - dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); + // check if OEM platform revision DSM call exists + exists = acpi_check_dsm(handle, &MSHW0040_DSM_UUID, + MSHW0040_DSM_REVISION, + BIT(MSHW0040_DSM_GET_OMPR)); - return 0; + return exists ? 0 : -ENODEV; } /* From 8d4b2e6ae2447ff3dbc8356973df78ee4a873c78 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:22:57 +1100 Subject: [PATCH 413/521] BACKPORT: SURFACE: platform/surface: surfacepro3_button: don't load on amd variant The AMD variant of the Surface Laptop report 0 for their OEM platform revision. The Surface devices that require the surfacepro3_button driver do not have the _DSM that gets the OEM platform revision. If the method does not exist, load surfacepro3_button. Fixes: 64dd243d7356 ("platform/x86: surfacepro3_button: Fix device check") Co-developed-by: Maximilian Luz Signed-off-by: Sachi King Patchset: surface-button Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/platform/surface/surfacepro3_button.c ] Signed-off-by: Mingcong Bai --- drivers/platform/surface/surfacepro3_button.c | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 388a3e1a488cb7..f94510dbda44dd 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -149,37 +149,18 @@ static int surface_button_resume(struct device *dev) /* * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device * ID (MSHW0040) for the power/volume buttons. Make sure this is the right - * device by checking for the _DSM method and OEM Platform Revision. + * device by checking for the _DSM method and OEM Platform Revision DSM + * function. * * Returns true if the driver should bind to this device, i.e. the device is * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. */ static bool surface_button_check_MSHW0040(struct device *dev, acpi_handle handle) { - union acpi_object *result; - u64 oem_platform_rev = 0; // valid revisions are nonzero - - // get OEM platform revision - result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, - MSHW0040_DSM_REVISION, - MSHW0040_DSM_GET_OMPR, - NULL, ACPI_TYPE_INTEGER); - - /* - * If evaluating the _DSM fails, the method is not present. This means - * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we - * should use this driver. We use revision 0 indicating it is - * unavailable. - */ - - if (result) { - oem_platform_rev = result->integer.value; - ACPI_FREE(result); - } - - dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); - - return oem_platform_rev == 0; + // make sure that OEM platform revision DSM call does not exist + return !acpi_check_dsm(handle, &MSHW0040_DSM_UUID, + MSHW0040_DSM_REVISION, + BIT(MSHW0040_DSM_GET_OMPR)); } From 9e2a8701aec127a13a74a2c30bbf7fbb7206e30a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 18 Feb 2023 01:02:49 +0100 Subject: [PATCH 414/521] SURFACE: USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3 Type-Cover The touchpad on the Type-Cover of the Surface Go 3 is sometimes not being initialized properly. Apply USB_QUIRK_DELAY_INIT to fix this issue. More specifically, the device in question is a fairly standard modern touchpad with pointer and touchpad input modes. During setup, the device needs to be switched from pointer- to touchpad-mode (which is done in hid-multitouch) to fully utilize it as intended. Unfortunately, however, this seems to occasionally fail silently, leaving the device in pointer-mode. Applying USB_QUIRK_DELAY_INIT seems to fix this. Link: https://github.com/linux-surface/linux-surface/issues/1059 Signed-off-by: Maximilian Luz Patchset: surface-typecover Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 87ee2d938bc038..5e8ee248da9bdb 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -228,6 +228,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Microsoft Surface Dock Ethernet (RTL8153 GigE) */ { USB_DEVICE(0x045e, 0x07c6), .driver_info = USB_QUIRK_NO_LPM }, + /* Microsoft Surface Go 3 Type-Cover */ + { USB_DEVICE(0x045e, 0x09b5), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */ { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME }, From 5b938288a3146f9c64fc537b2053de396a419ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Thu, 5 Nov 2020 13:09:45 +0100 Subject: [PATCH 415/521] SURFACE: hid/multitouch: Turn off Type Cover keyboard backlight when suspending The Type Cover for Microsoft Surface devices supports a special usb control request to disable or enable the built-in keyboard backlight. On Windows, this request happens when putting the device into suspend or resuming it, without it the backlight of the Type Cover will remain enabled for some time even though the computer is suspended, which looks weird to the user. So add support for this special usb control request to hid-multitouch, which is the driver that's handling the Type Cover. The reason we have to use a pm_notifier for this instead of the usual suspend/resume methods is that those won't get called in case the usb device is already autosuspended. Also, if the device is autosuspended, we have to briefly autoresume it in order to send the request. Doing that should be fine, the usb-core driver does something similar during suspend inside choose_wakeup(). To make sure we don't send that request to every device but only to devices which support it, add a new quirk MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER to hid-multitouch. For now this quirk is only enabled for the usb id of the Surface Pro 2017 Type Cover, which is where I confirmed that it's working. Patchset: surface-typecover Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/hid-multitouch.c | 100 ++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a02ca0bb98efbb..74a18881de2bc6 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -36,7 +36,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -49,6 +52,7 @@ MODULE_DESCRIPTION("HID multitouch panels"); MODULE_LICENSE("GPL"); #include "hid-ids.h" +#include "usbhid/usbhid.h" #include "hid-haptic.h" @@ -79,6 +83,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_APPLE_TOUCHBAR BIT(23) #define MT_QUIRK_YOGABOOK9I BIT(24) #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) +#define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(26) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -86,6 +91,8 @@ MODULE_LICENSE("GPL"); #define MT_BUTTONTYPE_CLICKPAD 0 #define MT_BUTTONTYPE_PRESSUREPAD 1 +#define MS_TYPE_COVER_FEATURE_REPORT_USAGE 0xff050086 + enum latency_mode { HID_LATENCY_NORMAL = 0, HID_LATENCY_HIGH = 1, @@ -187,6 +194,8 @@ struct mt_device { struct list_head applications; struct list_head reports; + + struct notifier_block pm_notifier; }; static void mt_post_parse_default_settings(struct mt_device *td, @@ -235,6 +244,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); #define MT_CLS_APPLE_TOUCHBAR 0x0114 #define MT_CLS_YOGABOOK9I 0x0115 #define MT_CLS_EGALAX_P80H84 0x0116 +#define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER 0x0117 #define MT_CLS_SIS 0x0457 #define MT_DEFAULT_MAXCONTACT 10 @@ -454,6 +464,16 @@ static const struct mt_class mt_classes[] = { MT_QUIRK_IGNORE_DUPLICATES | MT_QUIRK_CONTACT_CNT_ACCURATE, }, + { .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER, + .quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT | + MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_IGNORE_DUPLICATES | + MT_QUIRK_HOVERING | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_STICKY_FINGERS | + MT_QUIRK_WIN8_PTP_BUTTONS, + .export_all_inputs = true + }, { } }; @@ -1949,6 +1969,69 @@ static void mt_expired_timeout(struct timer_list *t) clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } +static void get_type_cover_backlight_field(struct hid_device *hdev, + struct hid_field **field) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_field *cur_field; + int i, j; + + rep_enum = &hdev->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + cur_field = rep->field[i]; + + for (j = 0; j < cur_field->maxusage; j++) { + if (cur_field->usage[j].hid + == MS_TYPE_COVER_FEATURE_REPORT_USAGE) { + *field = cur_field; + return; + } + } + } + } +} + +static void update_keyboard_backlight(struct hid_device *hdev, bool enabled) +{ + struct usb_device *udev = hid_to_usb_dev(hdev); + struct hid_field *field = NULL; + + /* Wake up the device in case it's already suspended */ + pm_runtime_get_sync(&udev->dev); + + get_type_cover_backlight_field(hdev, &field); + if (!field) { + hid_err(hdev, "couldn't find backlight field\n"); + goto out; + } + + field->value[field->index] = enabled ? 0x01ff00ff : 0x00ff00ff; + hid_hw_request(hdev, field->report, HID_REQ_SET_REPORT); + +out: + pm_runtime_put_sync(&udev->dev); +} + +static int mt_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, + void *unused) +{ + struct mt_device *td = + container_of(notifier, struct mt_device, pm_notifier); + struct hid_device *hdev = td->hdev; + + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT) { + if (pm_event == PM_SUSPEND_PREPARE) + update_keyboard_backlight(hdev, 0); + else if (pm_event == PM_POST_SUSPEND) + update_keyboard_backlight(hdev, 1); + } + + return NOTIFY_DONE; +} + static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, i; @@ -1977,6 +2060,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; hid_set_drvdata(hdev, td); + td->pm_notifier.notifier_call = mt_pm_notifier; + register_pm_notifier(&td->pm_notifier); + INIT_LIST_HEAD(&td->applications); INIT_LIST_HEAD(&td->reports); @@ -2015,8 +2101,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) timer_setup(&td->release_timer, mt_expired_timeout, 0); ret = hid_parse(hdev); - if (ret != 0) + if (ret != 0) { + unregister_pm_notifier(&td->pm_notifier); return ret; + } if (mtclass->name == MT_CLS_APPLE_TOUCHBAR && !hid_find_field(hdev, HID_INPUT_REPORT, @@ -2030,8 +2118,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) hdev->quirks |= HID_QUIRK_NOGET; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) + if (ret) { + unregister_pm_notifier(&td->pm_notifier); return ret; + } ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); if (ret) @@ -2092,6 +2182,7 @@ static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + unregister_pm_notifier(&td->pm_notifier); timer_delete_sync(&td->release_timer); sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); @@ -2559,6 +2650,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, + /* Microsoft Surface type cover */ + { .driver_data = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_MICROSOFT, 0x09c0) }, + /* Google MT devices */ { .driver_data = MT_CLS_GOOGLE, HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, From 5b9c7e8aec1458ba8dcd85cefc8268ba6d2b99dc Mon Sep 17 00:00:00 2001 From: PJungkamp Date: Fri, 25 Feb 2022 12:04:25 +0100 Subject: [PATCH 416/521] SURFACE: hid/multitouch: Add support for surface pro type cover tablet switch The Surface Pro Type Cover has several non standard HID usages in it's hid report descriptor. I noticed that, upon folding the typecover back, a vendor specific range of 4 32 bit integer hid usages is transmitted. Only the first byte of the message seems to convey reliable information about the keyboard state. 0x22 => Normal (keys enabled) 0x33 => Folded back (keys disabled) 0x53 => Rotated left/right side up (keys disabled) 0x13 => Cover closed (keys disabled) 0x43 => Folded back and Tablet upside down (keys disabled) This list may not be exhaustive. The tablet mode switch will be disabled for a value of 0x22 and enabled on any other value. Patchset: surface-typecover Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/hid-multitouch.c | 148 +++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 26 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 74a18881de2bc6..25e3425c0c0ada 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -84,6 +84,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_YOGABOOK9I BIT(24) #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) #define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(26) +#define MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH BIT(27) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -92,6 +93,8 @@ MODULE_LICENSE("GPL"); #define MT_BUTTONTYPE_PRESSUREPAD 1 #define MS_TYPE_COVER_FEATURE_REPORT_USAGE 0xff050086 +#define MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE 0xff050072 +#define MS_TYPE_COVER_APPLICATION 0xff050050 enum latency_mode { HID_LATENCY_NORMAL = 0, @@ -466,6 +469,7 @@ static const struct mt_class mt_classes[] = { }, { .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER, .quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT | + MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH | MT_QUIRK_ALWAYS_VALID | MT_QUIRK_IGNORE_DUPLICATES | MT_QUIRK_HOVERING | @@ -1513,6 +1517,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->application != HID_CP_CONSUMER_CONTROL && field->application != HID_GD_WIRELESS_RADIO_CTLS && field->application != HID_GD_SYSTEM_MULTIAXIS && + !(field->application == MS_TYPE_COVER_APPLICATION && + application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) && !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && application->quirks & MT_QUIRK_ASUS_CUSTOM_UP)) return -1; @@ -1540,6 +1547,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } + /* + * The Microsoft Surface Pro Typecover has a non-standard HID + * tablet mode switch on a vendor specific usage page with vendor + * specific usage. + */ + if (field->application == MS_TYPE_COVER_APPLICATION && + application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) { + usage->type = EV_SW; + usage->code = SW_TABLET_MODE; + *max = SW_MAX; + *bit = hi->input->swbit; + return 1; + } + if (rdata->is_mt_collection) return mt_touch_input_mapping(hdev, hi, field, usage, bit, max, application); @@ -1566,6 +1588,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, { struct mt_device *td = hid_get_drvdata(hdev); struct mt_report_data *rdata; + struct input_dev *input; rdata = mt_find_report_data(td, field->report); if (rdata && rdata->is_mt_collection) { @@ -1573,6 +1596,19 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, return -1; } + /* + * We own an input device which acts as a tablet mode switch for + * the Surface Pro Typecover. + */ + if (field->application == MS_TYPE_COVER_APPLICATION && + rdata->application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) { + input = hi->input; + input_set_capability(input, EV_SW, SW_TABLET_MODE); + input_report_switch(input, SW_TABLET_MODE, 0); + return -1; + } + /* let hid-core decide for the others */ return 0; } @@ -1582,11 +1618,21 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, { struct mt_device *td = hid_get_drvdata(hid); struct mt_report_data *rdata; + struct input_dev *input; rdata = mt_find_report_data(td, field->report); if (rdata && rdata->is_mt_collection) return mt_touch_event(hid, field, usage, value); + if (field->application == MS_TYPE_COVER_APPLICATION && + rdata->application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) { + input = field->hidinput->input; + input_report_switch(input, SW_TABLET_MODE, (value & 0xFF) != 0x22); + input_sync(input); + return 1; + } + return 0; } @@ -1801,6 +1847,42 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app) app->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } +static int get_type_cover_field(struct hid_report_enum *rep_enum, + struct hid_field **field, int usage) +{ + struct hid_report *rep; + struct hid_field *cur_field; + int i, j; + + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + cur_field = rep->field[i]; + if (cur_field->application != MS_TYPE_COVER_APPLICATION) + continue; + for (j = 0; j < cur_field->maxusage; j++) { + if (cur_field->usage[j].hid == usage) { + *field = cur_field; + return true; + } + } + } + } + return false; +} + +static void request_type_cover_tablet_mode_switch(struct hid_device *hdev) +{ + struct hid_field *field; + + if (get_type_cover_field(&hdev->report_enum[HID_INPUT_REPORT], + &field, + MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE)) { + hid_hw_request(hdev, field->report, HID_REQ_GET_REPORT); + } else { + hid_err(hdev, "couldn't find tablet mode field\n"); + } +} + static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); @@ -1859,6 +1941,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) /* force BTN_STYLUS to allow tablet matching in udev */ __set_bit(BTN_STYLUS, hi->input->keybit); break; + case MS_TYPE_COVER_APPLICATION: + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) { + suffix = "Tablet Mode Switch"; + request_type_cover_tablet_mode_switch(hdev); + break; + } + fallthrough; default: suffix = "UNKNOWN"; break; @@ -1969,30 +2058,6 @@ static void mt_expired_timeout(struct timer_list *t) clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } -static void get_type_cover_backlight_field(struct hid_device *hdev, - struct hid_field **field) -{ - struct hid_report_enum *rep_enum; - struct hid_report *rep; - struct hid_field *cur_field; - int i, j; - - rep_enum = &hdev->report_enum[HID_FEATURE_REPORT]; - list_for_each_entry(rep, &rep_enum->report_list, list) { - for (i = 0; i < rep->maxfield; i++) { - cur_field = rep->field[i]; - - for (j = 0; j < cur_field->maxusage; j++) { - if (cur_field->usage[j].hid - == MS_TYPE_COVER_FEATURE_REPORT_USAGE) { - *field = cur_field; - return; - } - } - } - } -} - static void update_keyboard_backlight(struct hid_device *hdev, bool enabled) { struct usb_device *udev = hid_to_usb_dev(hdev); @@ -2001,8 +2066,9 @@ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled) /* Wake up the device in case it's already suspended */ pm_runtime_get_sync(&udev->dev); - get_type_cover_backlight_field(hdev, &field); - if (!field) { + if (!get_type_cover_field(&hdev->report_enum[HID_FEATURE_REPORT], + &field, + MS_TYPE_COVER_FEATURE_REPORT_USAGE)) { hid_err(hdev, "couldn't find backlight field\n"); goto out; } @@ -2160,13 +2226,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state) static int mt_reset_resume(struct hid_device *hdev) { + struct mt_device *td = hid_get_drvdata(hdev); + mt_release_contacts(hdev); mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); + + /* Request an update on the typecover folding state on resume + * after reset. + */ + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) + request_type_cover_tablet_mode_switch(hdev); + return 0; } static int mt_resume(struct hid_device *hdev) { + struct mt_device *td = hid_get_drvdata(hdev); + /* Some Elan legacy devices require SET_IDLE to be set on resume. * It should be safe to send it to other devices too. * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */ @@ -2175,12 +2252,31 @@ static int mt_resume(struct hid_device *hdev) mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); + /* Request an update on the typecover folding state on resume. */ + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) + request_type_cover_tablet_mode_switch(hdev); + return 0; } static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + struct hid_field *field; + struct input_dev *input; + + /* Reset tablet mode switch on disconnect. */ + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) { + if (get_type_cover_field(&hdev->report_enum[HID_INPUT_REPORT], + &field, + MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE)) { + input = field->hidinput->input; + input_report_switch(input, SW_TABLET_MODE, 0); + input_sync(input); + } else { + hid_err(hdev, "couldn't find tablet mode field\n"); + } + } unregister_pm_notifier(&td->pm_notifier); timer_delete_sync(&td->release_timer); From 54fb32ad9821f38f5b50c9c00e67d7115f2e9663 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 19 Feb 2023 22:12:24 +0100 Subject: [PATCH 417/521] BACKPORT: SURFACE: PCI: Add quirk to prevent calling shutdown method Work around buggy EFI firmware: On some Microsoft Surface devices (Surface Pro 9 and Surface Laptop 5) the EFI ResetSystem call with EFI_RESET_SHUTDOWN doesn't function properly. Instead of shutting the system down, it returns and the system stays on. It turns out that this only happens after PCI shutdown callbacks ran for specific devices. Excluding those devices from the shutdown process makes the ResetSystem call work as expected. TODO: Maybe we can find a better way or the root cause of this? Not-Signed-off-by: Maximilian Luz Patchset: surface-shutdown [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in include/linux/pci.h ] Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/pci/pci-driver.c | 3 +++ drivers/pci/quirks.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 3 files changed, 40 insertions(+) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 682c34a174dade..5790d901033e84 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -543,6 +543,9 @@ static void pci_device_shutdown(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; + if (pci_dev->no_shutdown) + return; + pm_runtime_resume(dev); if (drv && drv->shutdown) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d048a94f2e4981..bf02bf255ee630 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6446,3 +6446,39 @@ static void quirk_sg2042_no_port_services(struct pci_dev *dev) dev->dev_flags |= PCI_DEV_FLAGS_NO_PORT_SERVICES; } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SOPHGO, 0x2042, quirk_sg2042_no_port_services); + +static const struct dmi_system_id no_shutdown_dmi_table[] = { + /* + * Systems on which some devices should not be touched during shutdown. + */ + { + .ident = "Microsoft Surface Pro 9", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Pro 9"), + }, + }, + { + .ident = "Microsoft Surface Laptop 5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 5"), + }, + }, + {} +}; + +static void quirk_no_shutdown(struct pci_dev *dev) +{ + if (!dmi_check_system(no_shutdown_dmi_table)) + return; + + dev->no_shutdown = 1; + pci_info(dev, "disabling shutdown ops for [%04x:%04x]\n", + dev->vendor, dev->device); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461e, quirk_no_shutdown); // Thunderbolt 4 USB Controller +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x462f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown); // Thunderbolt 4 NHI +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown); // GPU diff --git a/include/linux/pci.h b/include/linux/pci.h index c022efb28471bf..bfae14bc5e62a1 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -513,6 +513,7 @@ struct pci_dev { unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */ unsigned int aspm_os_control:1; /* Display of ROM attribute enabled? */ + unsigned int no_shutdown:1; /* Do not touch device on shutdown */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ From b6bdd88227f999c29d970e807d5a84b83ca5e6e8 Mon Sep 17 00:00:00 2001 From: LegendaryFire Date: Wed, 7 Jan 2026 01:06:25 +0100 Subject: [PATCH 418/521] SURFACE: PCI: Add Surface Laptop Studio 2 devices to shutdown ops quirk Link: https://github.com/linux-surface/linux-surface/pull/1948 Patchset: surface-shutdown Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index bf02bf255ee630..3c612bdbab517f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6465,6 +6465,13 @@ static const struct dmi_system_id no_shutdown_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 5"), }, }, + { + .ident = "Microsoft Surface Laptop Studio 2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Laptop Studio 2"), + }, + }, {} }; @@ -6482,3 +6489,9 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461f, quirk_no_shutdown); // Thu DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x462f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown); // Thunderbolt 4 NHI DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown); // GPU + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa76e, quirk_no_shutdown); // Thunderbolt 4 USB Controller +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa71e, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa73f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa73e, quirk_no_shutdown); // Thunderbolt 4 NHI +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa7a0, quirk_no_shutdown); // GPU From 17c41c0751308f2f0cdf3580b415a87d5a8ce65a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 12 Mar 2023 01:41:57 +0100 Subject: [PATCH 419/521] SURFACE: platform/surface: gpe: Add support for Surface Pro 9 Add the lid GPE used by the Surface Pro 9. Signed-off-by: Maximilian Luz Patchset: surface-gpe Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/surface_gpe.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c index b359413903b13c..b4496db79f392e 100644 --- a/drivers/platform/surface/surface_gpe.c +++ b/drivers/platform/surface/surface_gpe.c @@ -41,6 +41,11 @@ static const struct property_entry lid_device_props_l4F[] = { {}, }; +static const struct property_entry lid_device_props_l52[] = { + PROPERTY_ENTRY_U32("gpe", 0x52), + {}, +}; + static const struct property_entry lid_device_props_l57[] = { PROPERTY_ENTRY_U32("gpe", 0x57), {}, @@ -107,6 +112,18 @@ static const struct dmi_system_id dmi_lid_device_table[] = { }, .driver_data = (void *)lid_device_props_l4B, }, + { + /* + * We match for SKU here due to product name clash with the ARM + * version. + */ + .ident = "Surface Pro 9", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_9_2038"), + }, + .driver_data = (void *)lid_device_props_l52, + }, { .ident = "Surface Book 1", .matches = { From 0612fcdc062b28d78f2e90bef597fbb3f28742e0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:56:57 +0200 Subject: [PATCH 420/521] SURFACE: ACPI: delay enumeration of devices with a _DEP pointing to an INT3472 device The clk and regulator frameworks expect clk/regulator consumer-devices to have info about the consumed clks/regulators described in the device's fw_node. To work around cases where this info is not present in the firmware tables, which is often the case on x86/ACPI devices, both frameworks allow the provider-driver to attach info about consumers to the clks/regulators when registering these. This causes problems with the probe ordering wrt drivers for consumers of these clks/regulators. Since the lookups are only registered when the provider-driver binds, trying to get these clks/regulators before then results in a -ENOENT error for clks and a dummy regulator for regulators. One case where we hit this issue is camera sensors such as e.g. the OV8865 sensor found on the Microsoft Surface Go. The sensor uses clks, regulators and GPIOs provided by a TPS68470 PMIC which is described in an INT3472 ACPI device. There is special platform code handling this and setting platform_data with the necessary consumer info on the MFD cells instantiated for the PMIC under: drivers/platform/x86/intel/int3472. For this to work properly the ov8865 driver must not bind to the I2C-client for the OV8865 sensor until after the TPS68470 PMIC gpio, regulator and clk MFD cells have all been fully setup. The OV8865 on the Microsoft Surface Go is just one example, all X86 devices using the Intel IPU3 camera block found on recent Intel SoCs have similar issues where there is an INT3472 HID ACPI-device, which describes the clks and regulators, and the driver for this INT3472 device must be fully initialized before the sensor driver (any sensor driver) binds for things to work properly. On these devices the ACPI nodes describing the sensors all have a _DEP dependency on the matching INT3472 ACPI device (there is one per sensor). This allows solving the probe-ordering problem by delaying the enumeration (instantiation of the I2C-client in the ov8865 example) of ACPI-devices which have a _DEP dependency on an INT3472 device. The new acpi_dev_ready_for_enumeration() helper used for this is also exported because for devices, which have the enumeration_by_parent flag set, the parent-driver will do its own scan of child ACPI devices and it will try to enumerate those during its probe(). Code doing this such as e.g. the i2c-core-acpi.c code must call this new helper to ensure that it too delays the enumeration until all the _DEP dependencies are met on devices which have the new honor_deps flag set. Signed-off-by: Hans de Goede Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/acpi/scan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 3b0cf49acaed56..de8df157be0bcd 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2246,6 +2246,9 @@ static const char * const acpi_system_dev_ids[] = { static void acpi_default_enumeration(struct acpi_device *device) { + if (!acpi_dev_ready_for_enumeration(device)) + return; + /* * Do not enumerate devices with enumeration_by_parent flag set as * they will be enumerated by their respective parents. From e5be611ae7ba65c01549e44eca35c61890f2f6a4 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sun, 10 Oct 2021 20:57:02 +0200 Subject: [PATCH 421/521] SURFACE: platform/x86: int3472: Enable I2c daisy chain The TPS68470 PMIC has an I2C passthrough mode through which I2C traffic can be forwarded to a device connected to the PMIC as though it were connected directly to the system bus. Enable this mode when the chip is initialised. Signed-off-by: Daniel Scally Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/tps68470.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c index a77ed32abe55f1..3ef46bace18745 100644 --- a/drivers/platform/x86/intel/int3472/tps68470.c +++ b/drivers/platform/x86/intel/int3472/tps68470.c @@ -46,6 +46,13 @@ static int tps68470_chip_init(struct device *dev, struct regmap *regmap) return ret; } + /* Enable I2C daisy chain */ + ret = regmap_write(regmap, TPS68470_REG_S_I2C_CTL, 0x03); + if (ret) { + dev_err(dev, "Failed to enable i2c daisy chain\n"); + return ret; + } + dev_info(dev, "TPS68470 REVID: 0x%02x\n", version); return 0; From 11995347f244b2377b8e09aae0bf4ab56fc614cc Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Tue, 21 Mar 2023 13:45:26 +0000 Subject: [PATCH 422/521] SURFACE: media: i2c: Clarify that gain is Analogue gain in OV7251 Update the control ID for the gain control in the ov7251 driver to V4L2_CID_ANALOGUE_GAIN. Signed-off-by: Daniel Scally Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/i2c/ov7251.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 27afc3fc01752e..28b192c9a5ec5b 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1053,7 +1053,7 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE: ret = ov7251_set_exposure(ov7251, ctrl->val); break; - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: ret = ov7251_set_gain(ov7251, ctrl->val); break; case V4L2_CID_TEST_PATTERN: @@ -1579,7 +1579,7 @@ static int ov7251_init_ctrls(struct ov7251 *ov7251) ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_EXPOSURE, 1, 32, 1, 32); ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_GAIN, 16, 1023, 1, 16); + V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 16); v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov7251_test_pattern_menu) - 1, From 34fb62ad453e1b3b7806dbc57ceec63f95f582be Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Wed, 22 Mar 2023 11:01:42 +0000 Subject: [PATCH 423/521] SURFACE: media: v4l2-core: Acquire privacy led in v4l2_async_register_subdev() The current call to v4l2_subdev_get_privacy_led() is contained in v4l2_async_register_subdev_sensor(), but that function isn't used by all the sensor drivers. Move the acquisition of the privacy led to v4l2_async_register_subdev() instead. Signed-off-by: Daniel Scally Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/v4l2-core/v4l2-async.c | 4 ++++ drivers/media/v4l2-core/v4l2-fwnode.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 888a2e213b0885..f1043fce13e273 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -811,6 +811,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module) INIT_LIST_HEAD(&sd->asc_list); + ret = v4l2_subdev_get_privacy_led(sd); + if (ret < 0) + return ret; + /* * No reference taken. The reference is held by the device (struct * v4l2_subdev.dev), and async sub-device does not exist independently diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 77f3298821b5c6..10a3eb923fa2ee 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1270,10 +1270,6 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) v4l2_async_subdev_nf_init(notifier, sd); - ret = v4l2_subdev_get_privacy_led(sd); - if (ret < 0) - goto out_cleanup; - ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier); if (ret < 0) goto out_cleanup; From 26d7fd8b04859a180b69c459d064dfc3334fde6f Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 21 Mar 2023 23:37:16 +0800 Subject: [PATCH 424/521] BACKPORT: SURFACE: platform: x86: int3472: Add MFD cell for tps68470 LED Add MFD cell for tps68470-led. Reviewed-by: Daniel Scally Signed-off-by: Kate Hsuan Reviewed-by: Hans de Goede Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/platform/x86/intel/int3472/tps68470.c ] Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/tps68470.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c index 3ef46bace18745..8dece4261fc4c4 100644 --- a/drivers/platform/x86/intel/int3472/tps68470.c +++ b/drivers/platform/x86/intel/int3472/tps68470.c @@ -17,7 +17,7 @@ #define DESIGNED_FOR_CHROMEOS 1 #define DESIGNED_FOR_WINDOWS 2 -#define TPS68470_WIN_MFD_CELL_COUNT 3 +#define TPS68470_WIN_MFD_CELL_COUNT 4 static const struct mfd_cell tps68470_cros[] = { { .name = "tps68470-gpio" }, @@ -205,6 +205,7 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data); cells[2].name = "tps68470-gpio"; cells[2].swnode = board_data->tps68470_gpio_swnode; + cells[3].name = "tps68470-led"; for (i = 0; i < board_data->n_gpiod_lookups; i++) gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]); From c6d0b58afae3946c270912171f008468519ffe2f Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 21 Mar 2023 23:37:17 +0800 Subject: [PATCH 425/521] SURFACE: include: mfd: tps68470: Add masks for LEDA and LEDB Add flags for both LEDA(TPS68470_ILEDCTL_ENA), LEDB (TPS68470_ILEDCTL_ENB), and current control mask for LEDB (TPS68470_ILEDCTL_CTRLB) Reviewed-by: Daniel Scally Reviewed-by: Hans de Goede Signed-off-by: Kate Hsuan Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- include/linux/mfd/tps68470.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/mfd/tps68470.h b/include/linux/mfd/tps68470.h index 7807fa329db001..2d2abb25b944f4 100644 --- a/include/linux/mfd/tps68470.h +++ b/include/linux/mfd/tps68470.h @@ -34,6 +34,7 @@ #define TPS68470_REG_SGPO 0x22 #define TPS68470_REG_GPDI 0x26 #define TPS68470_REG_GPDO 0x27 +#define TPS68470_REG_ILEDCTL 0x28 #define TPS68470_REG_VCMVAL 0x3C #define TPS68470_REG_VAUX1VAL 0x3D #define TPS68470_REG_VAUX2VAL 0x3E @@ -94,4 +95,8 @@ #define TPS68470_GPIO_MODE_OUT_CMOS 2 #define TPS68470_GPIO_MODE_OUT_ODRAIN 3 +#define TPS68470_ILEDCTL_ENA BIT(2) +#define TPS68470_ILEDCTL_ENB BIT(6) +#define TPS68470_ILEDCTL_CTRLB GENMASK(5, 4) + #endif /* __LINUX_MFD_TPS68470_H */ From f7936d8dbeafeaf9ff63b301f4135d891ba235e2 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 21 Mar 2023 23:37:18 +0800 Subject: [PATCH 426/521] SURFACE: leds: tps68470: Add LED control for tps68470 There are two LED controllers, LEDA indicator LED and LEDB flash LED for tps68470. LEDA can be enabled by setting TPS68470_ILEDCTL_ENA. Moreover, tps68470 provides four levels of power status for LEDB. If the properties called "ti,ledb-current" can be found, the current will be set according to the property values. These two LEDs can be controlled through the LED class of sysfs (tps68470-leda and tps68470-ledb). Signed-off-by: Kate Hsuan Reviewed-by: Hans de Goede Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/leds/Kconfig | 12 +++ drivers/leds/Makefile | 1 + drivers/leds/leds-tps68470.c | 185 +++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 drivers/leds/leds-tps68470.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 22748404a7f061..7a1ad03e1db2c8 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -1022,6 +1022,18 @@ config LEDS_TPS6105X It is a single boost converter primarily for white LEDs and audio amplifiers. +config LEDS_TPS68470 + tristate "LED support for TI TPS68470" + depends on LEDS_CLASS + depends on INTEL_SKL_INT3472 + help + This driver supports TPS68470 PMIC with LED chip. + It provides two LED controllers, with the ability to drive 2 + indicator LEDs and 2 flash LEDs. + + To compile this driver as a module, choose M and it will be + called leds-tps68470 + config LEDS_IP30 tristate "LED support for SGI Octane machines" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 2b9506f9a69487..9d3cc8a0b6b460 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o +obj-$(CONFIG_LEDS_TPS68470) += leds-tps68470.o obj-$(CONFIG_LEDS_TURRIS_OMNIA) += leds-turris-omnia.o obj-$(CONFIG_LEDS_UPBOARD) += leds-upboard.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o diff --git a/drivers/leds/leds-tps68470.c b/drivers/leds/leds-tps68470.c new file mode 100644 index 00000000000000..35aeb5db89c8fe --- /dev/null +++ b/drivers/leds/leds-tps68470.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LED driver for TPS68470 PMIC + * + * Copyright (C) 2023 Red Hat + * + * Authors: + * Kate Hsuan + */ + +#include +#include +#include +#include +#include +#include + + +#define lcdev_to_led(led_cdev) \ + container_of(led_cdev, struct tps68470_led, lcdev) + +#define led_to_tps68470(led, index) \ + container_of(led, struct tps68470_device, leds[index]) + +enum tps68470_led_ids { + TPS68470_ILED_A, + TPS68470_ILED_B, + TPS68470_NUM_LEDS +}; + +static const char *tps68470_led_names[] = { + [TPS68470_ILED_A] = "tps68470-iled_a", + [TPS68470_ILED_B] = "tps68470-iled_b", +}; + +struct tps68470_led { + unsigned int led_id; + struct led_classdev lcdev; +}; + +struct tps68470_device { + struct device *dev; + struct regmap *regmap; + struct tps68470_led leds[TPS68470_NUM_LEDS]; +}; + +enum ctrlb_current { + CTRLB_2MA = 0, + CTRLB_4MA = 1, + CTRLB_8MA = 2, + CTRLB_16MA = 3, +}; + +static int tps68470_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) +{ + struct tps68470_led *led = lcdev_to_led(led_cdev); + struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id); + struct regmap *regmap = tps68470->regmap; + + switch (led->led_id) { + case TPS68470_ILED_A: + return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENA, + brightness ? TPS68470_ILEDCTL_ENA : 0); + case TPS68470_ILED_B: + return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENB, + brightness ? TPS68470_ILEDCTL_ENB : 0); + } + return -EINVAL; +} + +static enum led_brightness tps68470_brightness_get(struct led_classdev *led_cdev) +{ + struct tps68470_led *led = lcdev_to_led(led_cdev); + struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id); + struct regmap *regmap = tps68470->regmap; + int ret = 0; + int value = 0; + + ret = regmap_read(regmap, TPS68470_REG_ILEDCTL, &value); + if (ret) + return dev_err_probe(led_cdev->dev, -EINVAL, "failed on reading register\n"); + + switch (led->led_id) { + case TPS68470_ILED_A: + value = value & TPS68470_ILEDCTL_ENA; + break; + case TPS68470_ILED_B: + value = value & TPS68470_ILEDCTL_ENB; + break; + } + + return value ? LED_ON : LED_OFF; +} + + +static int tps68470_ledb_current_init(struct platform_device *pdev, + struct tps68470_device *tps68470) +{ + int ret = 0; + unsigned int curr; + + /* configure LEDB current if the properties can be got */ + if (!device_property_read_u32(&pdev->dev, "ti,ledb-current", &curr)) { + if (curr > CTRLB_16MA) { + dev_err(&pdev->dev, + "Invalid LEDB current value: %d\n", + curr); + return -EINVAL; + } + ret = regmap_update_bits(tps68470->regmap, TPS68470_REG_ILEDCTL, + TPS68470_ILEDCTL_CTRLB, curr); + } + return ret; +} + +static int tps68470_leds_probe(struct platform_device *pdev) +{ + int i = 0; + int ret = 0; + struct tps68470_device *tps68470; + struct tps68470_led *led; + struct led_classdev *lcdev; + + tps68470 = devm_kzalloc(&pdev->dev, sizeof(struct tps68470_device), + GFP_KERNEL); + if (!tps68470) + return -ENOMEM; + + tps68470->dev = &pdev->dev; + tps68470->regmap = dev_get_drvdata(pdev->dev.parent); + + for (i = 0; i < TPS68470_NUM_LEDS; i++) { + led = &tps68470->leds[i]; + lcdev = &led->lcdev; + + led->led_id = i; + + lcdev->name = devm_kasprintf(tps68470->dev, GFP_KERNEL, "%s::%s", + tps68470_led_names[i], LED_FUNCTION_INDICATOR); + if (!lcdev->name) + return -ENOMEM; + + lcdev->max_brightness = 1; + lcdev->brightness = 0; + lcdev->brightness_set_blocking = tps68470_brightness_set; + lcdev->brightness_get = tps68470_brightness_get; + lcdev->dev = &pdev->dev; + + ret = devm_led_classdev_register(tps68470->dev, lcdev); + if (ret) { + dev_err_probe(tps68470->dev, ret, + "error registering led\n"); + goto err_exit; + } + + if (i == TPS68470_ILED_B) { + ret = tps68470_ledb_current_init(pdev, tps68470); + if (ret) + goto err_exit; + } + } + +err_exit: + if (ret) { + for (i = 0; i < TPS68470_NUM_LEDS; i++) { + if (tps68470->leds[i].lcdev.name) + devm_led_classdev_unregister(&pdev->dev, + &tps68470->leds[i].lcdev); + } + } + + return ret; +} +static struct platform_driver tps68470_led_driver = { + .driver = { + .name = "tps68470-led", + }, + .probe = tps68470_leds_probe, +}; + +module_platform_driver(tps68470_led_driver); + +MODULE_ALIAS("platform:tps68470-led"); +MODULE_DESCRIPTION("LED driver for TPS68470 PMIC"); +MODULE_LICENSE("GPL v2"); From 2d622edaa7ae87c0817408346115d4169c4a6496 Mon Sep 17 00:00:00 2001 From: mojyack Date: Tue, 26 Mar 2024 05:55:44 +0900 Subject: [PATCH 427/521] SURFACE: media: i2c: dw9719: fix probe error on surface go 2 On surface go 2, sometimes probing dw9719 fails with "dw9719: probe of i2c-INT347A:00-VCM failed with error -121". The -121(-EREMOTEIO) is came from drivers/i2c/busses/i2c-designware-common.c:575, and indicates the initialize occurs too early. So just add some delay. There is no exact reason for this 10000us, but 100us failed. Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/i2c/dw9719.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c index 59558335989ed2..b89765521af5fa 100644 --- a/drivers/media/i2c/dw9719.c +++ b/drivers/media/i2c/dw9719.c @@ -121,6 +121,9 @@ static int dw9719_power_up(struct dw9719_device *dw9719, bool detect) if (ret) return ret; + /* Wait for device to be acknowledged */ + fsleep(10000); + /* * Need 100us to transition from SHUTDOWN to STANDBY. * Jiggle the SCL pin to wake up the device (even when the regulator is From e7956e7a454fad6b41d2b56c1afa1c0ffa8feb0b Mon Sep 17 00:00:00 2001 From: Tooraj Taraz Date: Wed, 31 Dec 2025 00:35:50 +0100 Subject: [PATCH 428/521] BACKPORT: SURFACE: Add camera support for Surface Pro 9 Experimental camera support for the Surface Pro 9. Link: https://github.com/linux-surface/linux-surface/pull/1867 Patchset: cameras [ Mingcong Bai: Since commit 2a7b7652b1bb ("platform/x86: int3472: Handle GPIO type 0x10 (DOVDD)") took the 0x10 entry used by the original patch for Surface Pro 9's secondary power rail, change the latter to 0x11 to adapt to this change. Link: https://github.com/linux-surface/linux-surface/issues/2057 Link: https://github.com/linux-surface/linux-surface/pull/2079 ] Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/i2c/ov13858.c | 158 +++++++++++++++++- drivers/media/i2c/ov5693.c | 1 + drivers/media/pci/intel/ipu-bridge.c | 4 + drivers/platform/x86/intel/int3472/discrete.c | 23 +++ 4 files changed, 184 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 162b49046990a1..85cd0c98af031e 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -2,6 +2,7 @@ // Copyright (c) 2017 Intel Corporation. #include +#include #include #include #include @@ -119,6 +120,14 @@ struct ov13858_mode { struct ov13858_reg_list reg_list; }; +// Or use standard names that might be more correct: +static const char * const ov13858_supply_names[] = { + "avdd", // Analog voltage + "pwr1", // Digital core voltage +}; + +#define OV13858_NUM_SUPPLIES ARRAY_SIZE(ov13858_supply_names) + /* 4224x3136 needs 1080Mbps/lane, 4 lanes */ static const struct ov13858_reg mipi_data_rate_1080mbps[] = { /* PLL1 registers */ @@ -1046,12 +1055,126 @@ struct ov13858 { /* Current mode */ const struct ov13858_mode *cur_mode; + struct regulator_bulk_data supplies[OV13858_NUM_SUPPLIES]; + struct gpio_desc *reset; + struct clk *xvclk; + /* Mutex for serialized access */ struct mutex mutex; }; #define to_ov13858(_sd) container_of(_sd, struct ov13858, sd) + +static int ov13858_get_regulators(struct ov13858 *ov13858) +{ + unsigned int i; + + for (i = 0; i < OV13858_NUM_SUPPLIES; i++) + ov13858->supplies[i].supply = ov13858_supply_names[i]; + + return devm_regulator_bulk_get(ov13858->dev, OV13858_NUM_SUPPLIES, + ov13858->supplies); +} + +static int ov13858_sensor_powerup(struct ov13858 *ov13858) +{ + int ret; + + dev_info(ov13858->dev, "Powering up sensor\n"); + + /* Assert reset */ + gpiod_set_value_cansleep(ov13858->reset, 1); + + /* Enable clock FIRST - sensor needs clock to communicate */ + ret = clk_prepare_enable(ov13858->xvclk); + if (ret) { + dev_err(ov13858->dev, "Failed to enable clock: %d\n", ret); + return ret; + } + dev_info(ov13858->dev, "Clock enabled\n"); + + /* Enable regulators */ + ret = regulator_bulk_enable(OV13858_NUM_SUPPLIES, ov13858->supplies); + if (ret) { + dev_err(ov13858->dev, "Failed to enable regulators: %d\n", ret); + clk_disable_unprepare(ov13858->xvclk); + return ret; + } + + dev_info(ov13858->dev, "Regulators enabled\n"); + + /* Wait for power to stabilize */ + usleep_range(5000, 10000); + + /* De-assert reset */ + gpiod_set_value_cansleep(ov13858->reset, 0); + + /* Wait for sensor to boot */ + msleep(20); + + dev_info(ov13858->dev, "Reset de-asserted, sensor should be ready\n"); + + return 0; +} + +static void ov13858_sensor_powerdown(struct ov13858 *ov13858) +{ + dev_info(ov13858->dev, "Powering down sensor\n"); + + /* Assert reset to put sensor in reset state */ + gpiod_set_value_cansleep(ov13858->reset, 1); + + /* Disable regulators */ + regulator_bulk_disable(OV13858_NUM_SUPPLIES, ov13858->supplies); + dev_info(ov13858->dev, "Regulators disabled\n"); + + /* Disable clock */ + clk_disable_unprepare(ov13858->xvclk); + dev_info(ov13858->dev, "Clock disabled\n"); +} + +static int __maybe_unused ov13858_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov13858 *ov13858 = to_ov13858(sd); + + ov13858_sensor_powerdown(ov13858); + + return 0; +} + +static int __maybe_unused ov13858_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov13858 *ov13858 = to_ov13858(sd); + int ret; + + ret = ov13858_sensor_powerup(ov13858); + if (ret) + return ret; + + return 0; +} + +static const struct dev_pm_ops ov13858_pm_ops = { + SET_RUNTIME_PM_OPS(ov13858_suspend, ov13858_resume, NULL) +}; + +static int ov13858_get_gpios(struct ov13858 *ov13858) +{ + ov13858->reset = devm_gpiod_get_optional(ov13858->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov13858->reset)) { + dev_err(ov13858->dev, "Error fetching reset GPIO\n"); + return PTR_ERR(ov13858->reset); + } + + return 0; +} + /* Read registers up to 4 at a time */ static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 *val) @@ -1498,8 +1621,11 @@ static int ov13858_identify_module(struct ov13858 *ov13858) int ret; u32 val; + dev_info(ov13858->dev, "Attempting to read chip ID from register 0x300a\n"); ret = ov13858_read_reg(ov13858, OV13858_REG_CHIP_ID, OV13858_REG_VALUE_24BIT, &val); + dev_info(ov13858->dev, "Chip ID read result: ret=%d, val=0x%06x (expected 0x%06x)\n", + ret, val, OV13858_CHIP_ID); if (ret) return ret; @@ -1509,6 +1635,7 @@ static int ov13858_identify_module(struct ov13858 *ov13858) return -EIO; } + dev_info(ov13858->dev, "Chip ID verified successfully!\n"); return 0; } @@ -1678,14 +1805,33 @@ static int ov13858_probe(struct i2c_client *client) "external clock %lu is not supported\n", freq); + /* Get the external clock */ + ov13858->xvclk = devm_clk_get_optional(ov13858->dev, "xvclk"); + if (IS_ERR(ov13858->xvclk)) + return dev_err_probe(ov13858->dev, PTR_ERR(ov13858->xvclk), + "failed to get xvclk\n"); + /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13858->sd, client, &ov13858_subdev_ops); + ret = ov13858_get_regulators(ov13858); + if (ret) + return dev_err_probe(ov13858->dev, ret, + "Error fetching regulators\n"); + + ret = ov13858_get_gpios(ov13858); + if (ret) + return ret; + + ret = ov13858_sensor_powerup(ov13858); + if (ret) + return ret; // No cleanup needed yet, devm handles GPIOs/regulators + /* Check module identity */ ret = ov13858_identify_module(ov13858); if (ret) { dev_err(ov13858->dev, "failed to find sensor: %d\n", ret); - return ret; + goto error_power_off; // Now we need to power down } /* Set default mode to max resolution */ @@ -1693,7 +1839,7 @@ static int ov13858_probe(struct i2c_client *client) ret = ov13858_init_controls(ov13858); if (ret) - return ret; + goto error_power_off; /* Initialize subdev */ ov13858->sd.internal_ops = &ov13858_internal_ops; @@ -1729,6 +1875,10 @@ static int ov13858_probe(struct i2c_client *client) error_handler_free: ov13858_free_controls(ov13858); + +error_power_off: + ov13858_sensor_powerdown(ov13858); + dev_err(ov13858->dev, "%s failed:%d\n", __func__, ret); return ret; @@ -1744,6 +1894,9 @@ static void ov13858_remove(struct i2c_client *client) ov13858_free_controls(ov13858); pm_runtime_disable(ov13858->dev); + if (!pm_runtime_status_suspended(ov13858->dev)) + ov13858_sensor_powerdown(ov13858); + pm_runtime_set_suspended(ov13858->dev); } static const struct i2c_device_id ov13858_id_table[] = { @@ -1765,6 +1918,7 @@ MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids); static struct i2c_driver ov13858_i2c_driver = { .driver = { .name = "ov13858", + .pm = &ov13858_pm_ops, .acpi_match_table = ACPI_PTR(ov13858_acpi_ids), }, .probe = ov13858_probe, diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 4cc796bbee9263..02236f3db19dc7 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -1396,6 +1396,7 @@ static const struct dev_pm_ops ov5693_pm_ops = { static const struct acpi_device_id ov5693_acpi_match[] = { {"INT33BE"}, + {"OVTI5693"}, {}, }; MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match); diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index fc6608e33de40a..7ce4692d33b871 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -61,6 +61,10 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), /* Onsemi MT9M114 */ IPU_SENSOR_CONFIG("INT33F0", 1, 384000000), + /* Omnivision OV5693 - Surface Pro 9 */ + IPU_SENSOR_CONFIG("OVTI5693", 1, 419200000), + /* Omnivision OV13858 - Surface Pro 9 */ + IPU_SENSOR_CONFIG("OVTID858", 4, 540000000), /* Omnivision OV2740 */ IPU_SENSOR_CONFIG("INT3474", 1, 180000000), /* Omnivision OV5670 */ diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 115bb37577a18d..b2a54733df2021 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -237,6 +237,14 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 /* Setups using a handshake pin need 25 ms enable delay */ *enable_time_us = 25 * USEC_PER_MSEC; break; + case 0x08: /* Surface Pro 9 - additional power rail */ + *con_id = "pwr1"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; + case 0x11: /* Surface Pro 9 - secondary power rail */ + *con_id = "pwr2"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; default: *con_id = "unknown"; *gpio_flags = GPIO_ACTIVE_HIGH; @@ -345,6 +353,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, case INT3472_GPIO_TYPE_POWER_ENABLE: case INT3472_GPIO_TYPE_DOVDD: case INT3472_GPIO_TYPE_HANDSHAKE: + case 0x08: /* Surface Pro 9 power rails */ + case 0x11: gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags); if (IS_ERR(gpio)) { ret = PTR_ERR(gpio); @@ -377,6 +387,19 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, err_msg = "Failed to register regulator\n"; break; + case 0x08: /* Surface Pro 9 - treat as power*/ + dev_info(int3472->dev, "GPIO type 0x%02x detected on pin 0x%02x\n", type, agpio->pin_table[0]); + dev_info(int3472->dev, " con_id=%s, flags=0x%x\n", con_id, gpio_flags); + ret = skl_int3472_register_regulator(int3472, gpio, + GPIO_REGULATOR_ENABLE_TIME, + con_id, NULL); + dev_info(int3472->dev, " register_regulator returned: %d\n", ret); + if (ret) { + dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", type, ret); + } + break; + case 0x11: + break; default: /* Never reached */ ret = -EINVAL; break; From 20558eedadf5a278588d4744579319e8b2992deb Mon Sep 17 00:00:00 2001 From: gabrielstedman Date: Fri, 13 Mar 2026 08:52:07 +0000 Subject: [PATCH 429/521] SURFACE: media: ipu-bridge: Add front camera rotation quirks for Surface Pro 8 and 9 On the Surface Pro 8 and 9, camera rotation in `ssdb.degree` is `0` (no rotation) but should be `180`. Add these devices to the respective quirk list. Link: https://github.com/linux-surface/kernel/pull/160 Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/pci/intel/ipu-bridge.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 7ce4692d33b871..209767f44bd21f 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -138,6 +138,20 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "OVTI02C1", }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Pro 8"), + }, + .driver_data = "INT33BE", + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Pro 9"), + }, + .driver_data = "OVTI5693", + }, {} /* Terminating entry */ }; From 086c227429bcaa8ecc7f69595417ea732bc33b5b Mon Sep 17 00:00:00 2001 From: Sachi King Date: Sat, 29 May 2021 17:47:38 +1000 Subject: [PATCH 430/521] SURFACE: ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7 override This patch is the work of Thomas Gleixner and is copied from: https://lore.kernel.org/lkml/87lf8ddjqx.ffs@nanos.tec.linutronix.de/ This patch adds a quirk to the ACPI setup to patch in the the irq 7 pin setup that is missing in the laptops ACPI table. This patch was used for validation of the issue, and is not a proper fix, but is probably a better temporary hack than continuing to probe the Legacy PIC and run with the PIC in an unknown state. Patchset: amd-gpio Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- arch/x86/kernel/acpi/boot.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index ceba24f65ae3a8..b9bb3748642e93 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -1176,6 +1177,17 @@ static void __init mp_config_acpi_legacy_irqs(void) } } +static const struct dmi_system_id surface_quirk[] __initconst = { + { + .ident = "Microsoft Surface Laptop 4 (AMD)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953") + }, + }, + {} +}; + /* * Parse IOAPIC related entries in MADT * returns 0 on success, < 0 on error @@ -1232,6 +1244,11 @@ static int __init acpi_parse_madt_ioapic_entries(void) acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0, acpi_gbl_FADT.sci_interrupt); + if (dmi_check_system(surface_quirk)) { + pr_warn("Surface hack: Override irq 7\n"); + mp_override_legacy_irq(7, 3, 3, 7); + } + /* Fill in identity legacy mappings where no override */ mp_config_acpi_legacy_irqs(); From b6198cc879a4836b64f2fd627599c2b0a12c70c3 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 3 Jun 2021 14:04:26 +0200 Subject: [PATCH 431/521] SURFACE: ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override quirk The 13" version of the Surface Laptop 4 has the same problem as the 15" version, but uses a different SKU. Add that SKU to the quirk as well. Patchset: amd-gpio Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- arch/x86/kernel/acpi/boot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index b9bb3748642e93..07384e7718e7e1 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1179,12 +1179,19 @@ static void __init mp_config_acpi_legacy_irqs(void) static const struct dmi_system_id surface_quirk[] __initconst = { { - .ident = "Microsoft Surface Laptop 4 (AMD)", + .ident = "Microsoft Surface Laptop 4 (AMD 15\")", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953") }, }, + { + .ident = "Microsoft Surface Laptop 4 (AMD 13\")", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1958:1959") + }, + }, {} }; From 735e77dbc69c116fdba335c88ca1e6380bfebb4e Mon Sep 17 00:00:00 2001 From: "Bart Groeneveld | GPX Solutions B.V" Date: Mon, 5 Dec 2022 16:08:46 +0100 Subject: [PATCH 432/521] BACKPORT: SURFACE: acpi: allow usage of acpi_tad on HW-reduced platforms The specification [1] allows so-called HW-reduced platforms, which do not implement everything, especially the wakeup related stuff. In that case, it is still usable as a RTC. This is helpful for [2] and [3], which is about a device with no other working RTC, but it does have an HW-reduced TAD, which can be used as a RTC instead. [1]: https://uefi.org/specs/ACPI/6.5/09_ACPI_Defined_Devices_and_Device_Specific_Objects.html#time-and-alarm-device [2]: https://bugzilla.kernel.org/show_bug.cgi?id=212313 [3]: https://github.com/linux-surface/linux-surface/issues/415 Signed-off-by: Bart Groeneveld | GPX Solutions B.V. Patchset: rtc Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai [ Mingcong Bai: Attempted to revise this patch since commit 8be4a29b495e ("ACPI: TAD: Create one attribute group") ] Signed-off-by: Mingcong Bai --- drivers/acpi/acpi_tad.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 386fc1abcbdcb5..e2259938e27892 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -570,7 +570,6 @@ static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(dc_status); static struct attribute *acpi_tad_attrs[] = { - &dev_attr_caps.attr, &dev_attr_ac_alarm.attr, &dev_attr_ac_policy.attr, &dev_attr_ac_status.attr, @@ -835,11 +834,6 @@ static int acpi_tad_probe(struct platform_device *pdev) return -ENODEV; } - if (!acpi_has_method(handle, "_PRW")) { - dev_info(dev, "Missing _PRW\n"); - caps &= ~(ACPI_TAD_AC_WAKE | ACPI_TAD_DC_WAKE); - } - if (!(caps & ACPI_TAD_AC_WAKE)) caps &= ~ACPI_TAD_DC_WAKE; From 7d252e2c841c6cd5f66f1c8b0d8ba7b52eb19af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciar=C3=A1n=20Coffey?= Date: Fri, 10 Oct 2025 19:04:03 +0100 Subject: [PATCH 433/521] SURFACE: HID: Add hid-surface driver to filter BTN_0 (FN key) The Surface Aggregator Module firmware incorrectly reports the FN key as BTN_0 (button 256) through runtime HID events. This button can get stuck in pressed state, flooding the input system and breaking focus tracking in KWin Wayland compositor. Add a new hid-surface driver that filters BTN_0 events using the event callback. The FN key should be handled as a hardware modifier and not reported to the OS. Tested on Surface Laptop 4 AMD with KDE Plasma Wayland. After the fix: - FN key combinations work correctly (volume, brightness) - Focus tracking no longer breaks - Mouse clicks work on all windows Fixes: https://github.com/linux-surface/linux-surface/issues/1851 Patchset: hid-surface Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/Kconfig | 8 ++++ drivers/hid/Makefile | 1 + drivers/hid/hid-surface.c | 90 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 drivers/hid/hid-surface.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 2886bde63605c3..1640864b9c8e13 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1159,6 +1159,14 @@ config HID_SUNPLUS help Support for Sunplus wireless desktop. +config HID_SURFACE + tristate "Microsoft Surface" + depends on SURFACE_AGGREGATOR + help + Say Y here to enable HID driver for Microsoft Surface integrated + keyboard and touchpad. This driver filters out erroneous BTN_0 + (FN key) events that can cause input focus issues. + config HID_RMI tristate "Synaptics RMI4 device support" select RMI4_CORE diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fdc4e3d459d15a..e71461250b981f 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -178,6 +178,7 @@ obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ +obj-$(CONFIG_HID_SURFACE) += hid-surface.o obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ diff --git a/drivers/hid/hid-surface.c b/drivers/hid/hid-surface.c new file mode 100644 index 00000000000000..a171ea65672fea --- /dev/null +++ b/drivers/hid/hid-surface.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Microsoft Surface devices + * + * Copyright (c) 2025 Linux Surface Project + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +/* + * The Surface Aggregator Module firmware incorrectly reports the FN key + * as BTN_0 (button 256). This button can get stuck in pressed state, + * flooding the input system and breaking focus tracking in some + * compositors. Filter out BTN_0 as FN should be handled as a hardware + * modifier, not reported to the OS. + */ +static int surface_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* + * Filter BTN_0 during input mapping in case it appears in the + * HID descriptor (defense in depth). + */ + if (usage->type == EV_KEY && usage->code == BTN_0) + return -1; /* Don't map this usage */ + + return 0; /* Use default mapping */ +} + +static int surface_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + /* + * The Surface Aggregator Module firmware reports the FN key as BTN_0 + * at runtime. This button can get stuck in pressed state, flooding + * the input system and breaking focus tracking. Filter out these + * events as FN should be a hardware modifier, not reported to the OS. + */ + if (usage->type == EV_KEY && usage->code == BTN_0) + return 1; /* Event handled, don't process further */ + + return 0; /* Process event normally */ +} + +static int surface_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + return 0; +} + +static const struct hid_device_id surface_devices[] = { + { HID_DEVICE(BUS_HOST, HID_GROUP_GENERIC, + USB_VENDOR_ID_MICROSOFT, 0x09AE) }, /* Surface Keyboard */ + { HID_DEVICE(BUS_HOST, HID_GROUP_GENERIC, + USB_VENDOR_ID_MICROSOFT, 0x09AF) }, /* Surface Mouse/Touchpad */ + { } +}; +MODULE_DEVICE_TABLE(hid, surface_devices); + +static struct hid_driver surface_driver = { + .name = "surface", + .id_table = surface_devices, + .probe = surface_probe, + .input_mapping = surface_input_mapping, + .event = surface_event, +}; +module_hid_driver(surface_driver); + +MODULE_AUTHOR("Linux Surface Project"); +MODULE_DESCRIPTION("Microsoft Surface HID driver"); +MODULE_LICENSE("GPL"); From 9ec07cfb98c72d12f0e4de39cc6e8c1f3c12ba73 Mon Sep 17 00:00:00 2001 From: LegendaryFire Date: Fri, 13 Mar 2026 10:16:37 +0100 Subject: [PATCH 434/521] SURFACE: hid/multitouch: Prevent mode set on Surface Laptop Studio 2 touch pad Note: Is this still required with MT_QUIRK_KEEP_LATENCY_ON_CLOSE now being available? Patchset: hid-surface Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/hid-multitouch.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 25e3425c0c0ada..944ed209088882 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -85,6 +85,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) #define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(26) #define MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH BIT(27) +#define MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE BIT(28) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -248,6 +249,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); #define MT_CLS_YOGABOOK9I 0x0115 #define MT_CLS_EGALAX_P80H84 0x0116 #define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER 0x0117 +#define MT_CLS_SURFACE_TOUCHPAD 0x0118 #define MT_CLS_SIS 0x0457 #define MT_DEFAULT_MAXCONTACT 10 @@ -478,6 +480,10 @@ static const struct mt_class mt_classes[] = { MT_QUIRK_WIN8_PTP_BUTTONS, .export_all_inputs = true }, + { .name = MT_CLS_SURFACE_TOUCHPAD, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE + }, { } }; @@ -2287,6 +2293,17 @@ static void mt_remove(struct hid_device *hdev) static void mt_on_hid_hw_open(struct hid_device *hdev) { + struct mt_device *td = hid_get_drvdata(hdev); + + /* + * Some devices (e.g. Surface Laptop Studio 2 touchpad) can get stuck + * non-functional if we change touchpad reporting modes from the HID + * open/close hooks. Avoid mode switching on hw_open/hw_close for + * those devices. + */ + if (td && td->mtclass.quirks & MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE) + return; + mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); } @@ -2294,6 +2311,15 @@ static void mt_on_hid_hw_close(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + /* + * Some devices (e.g. Surface Laptop Studio 2 touchpad) can get stuck + * non-functional if we change touchpad reporting modes from the HID + * open/close hooks. Avoid mode switching on hw_open/hw_close for + * those devices. + */ + if (td && td->mtclass.quirks & MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE) + return; + if (td->mtclass.quirks & MT_QUIRK_KEEP_LATENCY_ON_CLOSE) mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_NONE); else @@ -2751,6 +2777,11 @@ static const struct hid_device_id mt_devices[] = { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_MICROSOFT, 0x09c0) }, + /* Microsoft Surface touch pad */ + { .driver_data = MT_CLS_SURFACE_TOUCHPAD, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_MICROSOFT, 0x0C46) }, + /* Google MT devices */ { .driver_data = MT_CLS_GOOGLE, HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, From 92617ac6d5e93bffc0b64867df0ced3cba27c0ed Mon Sep 17 00:00:00 2001 From: Han Gao Date: Mon, 24 Nov 2025 20:38:44 +0800 Subject: [PATCH 435/521] XUANTIE: riscv: dts: th1520: add licheepi4a 16g support From: https://github.com/revyos/th1520-linux-kernel/commit/01a510898e41e704bee1fe58a2c0c0a29cb96548 Signed-off-by: Han Gao Signed-off-by: Mingcong Bai --- arch/riscv/boot/dts/thead/Makefile | 1 + .../boot/dts/thead/th1520-lichee-pi-4a-16g.dts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts diff --git a/arch/riscv/boot/dts/thead/Makefile b/arch/riscv/boot/dts/thead/Makefile index b55a17127c2bc0..281849e71ccb84 100644 --- a/arch/riscv/boot/dts/thead/Makefile +++ b/arch/riscv/boot/dts/thead/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_THEAD) += th1520-lichee-pi-4a.dtb th1520-beaglev-ahead.dtb +dtb-$(CONFIG_ARCH_THEAD) += th1520-lichee-pi-4a-16g.dtb diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts new file mode 100644 index 00000000000000..a3a991baf716be --- /dev/null +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2023 Han Gao + */ + +/dts-v1/; + +#include "th1520-lichee-pi-4a.dts" + +/ { + model = "Sipeed Lichee Pi 4A 16G"; + compatible = "sipeed,lichee-pi-4a", "sipeed,lichee-module-4a", "thead,th1520"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x00000000 0x4 0x00000000>; + }; +}; From b8db3fe8409ec7b3be3d4ef8f8f16edbabc4fd5c Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 14 May 2025 08:16:15 +0800 Subject: [PATCH 436/521] REVYOS: HACK: riscv: dts: th1520: rename thead to xuantie From: https://github.com/revyos/th1520-linux-kernel/commit/fe55f7b77b81749bfd2a5eac9328e016f299d7a6 HACK patch for compatibility with thead-u-boot and vendor opensbi Signed-off-by: Han Gao [Icenowy: preserve the original compatible to allow Linux to match] Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/riscv/boot/dts/thead/th1520.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index aa757ddd604672..108205d5d4a66b 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -286,7 +286,7 @@ }; aon: aon { - compatible = "thead,th1520-aon"; + compatible = "xuantie,th1520-aon", "thead,th1520-aon"; mboxes = <&mbox_910t 1>; mbox-names = "aon"; resets = <&rst TH1520_RESET_ID_GPU_CLKGEN>; From 89cf7e1c4cac441689bb6e2d2edea274d273784c Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 14 May 2025 08:27:18 +0800 Subject: [PATCH 437/521] REVYOS: HACK: riscv: dts: th1520: add xuantie,th1520-mbox-r From: https://github.com/revyos/th1520-linux-kernel/commit/1e57185ba18320bb4fb747a6089f9404f970964c HACK patch for compatibility with thead-u-boot and vendor opensbi Signed-off-by: Han Gao [Icenowy: remove the interrupt-controller property] Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/riscv/boot/dts/thead/th1520.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index 108205d5d4a66b..fdef5fb82b451c 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -292,6 +292,24 @@ resets = <&rst TH1520_RESET_ID_GPU_CLKGEN>; reset-names = "gpu-clkgen"; #power-domain-cells = <1>; + opensbi-mboxes = <&mbox_910r>; + status = "okay"; + }; + + mbox_910r: mbox@ffefc53000 { + compatible = "xuantie,th1520-mbox-r"; + reg = <0xff 0xefc53000 0x0 0x4000>, + <0xff 0xefc3f000 0x0 0x1000>, + <0xff 0xefc47000 0x0 0x1000>, + <0xff 0xefc4f000 0x0 0x1000>; + reg-names = "local_base", + "remote_icu0", + "remote_icu1", + "remote_icu2"; + clocks = <&clk CLK_PERI_APB_PCLK>; + clock-names = "ipg"; + icu_cpu_id = <3>; + #mbox-cells = <2>; }; soc { From c864682c185fc2af70f9a9eb4f706db055d3a119 Mon Sep 17 00:00:00 2001 From: Zhang Meng Date: Mon, 5 Jan 2026 20:05:04 +0800 Subject: [PATCH 438/521] SPACEMIT: riscv: uaccess: don't use vector if buffer is not cacheable FROM: https://github.com/spacemit-com/linux-6.18/commit/9168f7e0c6bfdcfa3b6a64a4d45e3cd68a81618f Change-Id: I040d597ee246777767f7be747fa9202154524538 [ Vivian: Rebase and move check into enter_vector_usercopy ] Signed-off-by: Vivian Wang Signed-off-by: Han Gao --- arch/riscv/include/asm/uaccess.h | 5 +++ arch/riscv/lib/Makefile | 1 + arch/riscv/lib/riscv_v_helpers.c | 5 +++ arch/riscv/lib/uaccess_cache_check.c | 65 ++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 arch/riscv/lib/uaccess_cache_check.c diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h index 11c9886c3b704b..69bdc070f4204b 100644 --- a/arch/riscv/include/asm/uaccess.h +++ b/arch/riscv/include/asm/uaccess.h @@ -487,6 +487,11 @@ static inline void user_access_restore(unsigned long enabled) { } if (__asm_copy_from_user_sum_enabled(_dst, _src, _len)) \ goto label; +/* Memory cacheability check for vector uaccess optimization */ +#ifdef CONFIG_RISCV_ISA_V +int is_cacheable_safe(const void *addr); +#endif + #else /* CONFIG_MMU */ #include #endif /* CONFIG_MMU */ diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 6f767b2a349d76..01c53576be98dd 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -14,6 +14,7 @@ endif lib-y += csum.o ifeq ($(CONFIG_MMU), y) lib-$(CONFIG_RISCV_ISA_V) += uaccess_vector.o +lib-$(CONFIG_RISCV_ISA_V) += uaccess_cache_check.o endif lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o diff --git a/arch/riscv/lib/riscv_v_helpers.c b/arch/riscv/lib/riscv_v_helpers.c index 7bbdfc6d4552d4..7ab2cea280f8bb 100644 --- a/arch/riscv/lib/riscv_v_helpers.c +++ b/arch/riscv/lib/riscv_v_helpers.c @@ -8,6 +8,7 @@ #include #include +#include #ifdef CONFIG_MMU #include @@ -28,6 +29,10 @@ asmlinkage int enter_vector_usercopy(void *dst, void *src, size_t n, if (!may_use_simd()) goto fallback; + /* HACK */ + if (!is_cacheable_safe(dst) || !is_cacheable_safe(src)) + goto fallback; + kernel_vector_begin(); remain = enable_sum ? __asm_vector_usercopy(dst, src, n) : __asm_vector_usercopy_sum_enabled(dst, src, n); diff --git a/arch/riscv/lib/uaccess_cache_check.c b/arch/riscv/lib/uaccess_cache_check.c new file mode 100644 index 00000000000000..0b0996fa2d371a --- /dev/null +++ b/arch/riscv/lib/uaccess_cache_check.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Memory cacheability check for RISC-V uaccess optimization + * + * This file provides a C function that can be called from assembly + * to determine if a buffer is cacheable before using vector instructions. + */ + +#include +#include +#include + +/** + * is_cacheable_safe - Check if memory buffer is cacheable + * @addr: Virtual address to check (kernel or user space) + * + * Returns: 1 if cacheable, 0 if non-cacheable + * + * This function is designed to be called from assembly code in uaccess.S + * to determine if vector instructions are safe to use for memory copy. + * + * Non-cacheable memory (device IO, DMA coherent buffers) should not use + * vector instructions as they may cause cache coherency issues. + * + * Handles both kernel and user space addresses safely: + * - Kernel direct mapping: Always cacheable + * - Kernel vmalloc: Check VM flags + * - User space: Check page table (most are cacheable) + * - ioremap/DMA: Non-cacheable + */ +int is_cacheable_safe(const void *addr) +{ + unsigned long vaddr = (unsigned long)addr; + + /* Kernel direct mapped memory - always cacheable */ + if (virt_addr_valid(addr)) + return 1; + + if (vaddr < TASK_SIZE) { + /* + * User space address, Determine it as a cacheable buffer, + * maybe not safe!! + */ + return 1; + } + + /* Check if it's a vmalloc region (kernel virtual address) */ + if (is_vmalloc_addr(addr)) { + struct vm_struct *vm; + + vm = find_vm_area(addr); + if (!vm) + return 0; + + /* Exclude ioremap and DMA coherent buffers */ + if (vm->flags & (VM_IOREMAP | VM_DMA_COHERENT)) + return 0; + + /* Normal vmalloc - cacheable */ + return 1; + } + + /* Unknown kernel region - assume non-cacheable for safety */ + return 0; +} From 05e294a67e45740ba5d88eb22ca5468905994a9f Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 19 May 2026 19:57:52 +0800 Subject: [PATCH 439/521] RUYI: drm/amdgpu: disable dynamic PCIe speed switch on SpacemiT K3 The dynamic speed switch functionality seems to be broken on SpacemiT K3, and leads to frequent GPU crashes at least with Polaris GPUs. Disable dynamic speed switch on this platform. Signed-off-by: Icenowy Zheng Signed-off-by: Han Gao --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index feab90e3efd1e2..5deffe2773085e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1309,6 +1309,14 @@ bool amdgpu_device_seamless_boot_supported(struct amdgpu_device *adev) */ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device *adev) { + struct pci_dev *parent = adev->pdev; + static const struct pci_device_id broken_devids[] = { + /* SpacemiT K3 */ + { PCI_DEVICE(PCI_VENDOR_ID_SPACEMIT, + PCI_DEVICE_ID_SPACEMIT_K3) }, + {} + }; + #if IS_ENABLED(CONFIG_X86) struct cpuinfo_x86 *c = &cpu_data(0); @@ -1319,6 +1327,17 @@ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device if (c->x86_vendor == X86_VENDOR_INTEL) return false; #endif + /* skip upstream/downstream switches internal to dGPU */ + while (parent && parent->vendor == PCI_VENDOR_ID_ATI) { + parent = pci_upstream_bridge(parent); + } + + if (!parent) + return true; + + if (pci_match_id(broken_devids, parent)) + return false; + return true; } From 4bb08853506bfaab91961420f12084d4ffdecac8 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 22 Sep 2024 01:07:11 +0800 Subject: [PATCH 440/521] AOSCOS: drm: amdgpu: radeon: disable cache flush workaround for LoongArch and Loongson64 This workaround causes instability for LoongArch/Loongson (MIPS) devices based on the 7A1000/2000 chipset under heavy I/O load. FIXME: Disable this workaround until we find a better fix (possibly in the platform-specific PCI code). Signed-off-by: Mingcong Bai Link: https://lore.kernel.org/all/20240617105846.1516006-1-uwu@icenowy.me/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 8 ++++++++ drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 8 ++++++++ drivers/gpu/drm/radeon/cik.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 2b691452775bc4..083f9e8e1862e0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2117,6 +2117,13 @@ static void gfx_v7_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, bool int_sel = flags & AMDGPU_FENCE_FLAG_INT; bool exec = flags & AMDGPU_FENCE_FLAG_EXEC; +/* This workaround causes instability for LoongArch/Loongson (MIPS) + * devices based on the 7A1000/2000 chipset under heavy I/O load. + * + * FIXME: Disable this workaround until we find a better fix (possibly in + * the platform-specific PCI code). + */ +#ifndef CONFIG_MACH_LOONGSON64 /* Workaround for cache flush problems. First send a dummy EOP * event down the pipe with seq one below. */ @@ -2130,6 +2137,7 @@ static void gfx_v7_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, DATA_SEL(1) | INT_SEL(0)); amdgpu_ring_write(ring, lower_32_bits(seq - 1)); amdgpu_ring_write(ring, upper_32_bits(seq - 1)); +#endif /* Then send the real EOP event down the pipe. */ amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index a6b4c8f41dc116..892f239e092191 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -6139,6 +6139,13 @@ static void gfx_v8_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, bool int_sel = flags & AMDGPU_FENCE_FLAG_INT; bool exec = flags & AMDGPU_FENCE_FLAG_EXEC; +/* This workaround causes instability for LoongArch/Loongson (MIPS) + * devices based on the 7A1000/2000 chipset under heavy I/O load. + * + * FIXME: Disable this workaround until we find a better fix (possibly in + * the platform-specific PCI code). + */ +#ifndef CONFIG_MACH_LOONGSON64 /* Workaround for cache flush problems. First send a dummy EOP * event down the pipe with seq one below. */ @@ -6153,6 +6160,7 @@ static void gfx_v8_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, DATA_SEL(1) | INT_SEL(0)); amdgpu_ring_write(ring, lower_32_bits(seq - 1)); amdgpu_ring_write(ring, upper_32_bits(seq - 1)); +#endif /* Then send the real EOP event down the pipe: * EVENT_WRITE_EOP - flush caches, send int */ diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 0b804e951b7b6f..7e7f6248b63a63 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3543,6 +3543,13 @@ void cik_fence_gfx_ring_emit(struct radeon_device *rdev, struct radeon_ring *ring = &rdev->ring[fence->ring]; u64 addr = rdev->fence_drv[fence->ring].gpu_addr; +/* This workaround causes instability for LoongArch/Loongson (MIPS) + * devices based on the 7A1000/2000 chipset under heavy I/O load. + * + * FIXME: Disable this workaround until we find a better fix (possibly in + * the platform-specific PCI code). + */ +#ifndef CONFIG_MACH_LOONGSON64 /* Workaround for cache flush problems. First send a dummy EOP * event down the pipe with seq one below. */ @@ -3556,6 +3563,7 @@ void cik_fence_gfx_ring_emit(struct radeon_device *rdev, DATA_SEL(1) | INT_SEL(0)); radeon_ring_write(ring, fence->seq - 1); radeon_ring_write(ring, 0); +#endif /* Then send the real EOP event down the pipe. */ radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); From 71e09774d6614a97122eb74d260ca31e2400256b Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 22 Sep 2024 03:19:19 +0800 Subject: [PATCH 441/521] AOSCOS: net: stmmac: Make phytium_dwmac_remove() return void Fixes: 731b13ef92e8 ("BACKPORT: PHYTIUM: net/stmmac: Add phytium DWMAC driver support v2") Fixes: 0edb555a65d1 ("platform: Make platform_driver::remove() return void") Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 7adeddd82d33c7..fb6929552380d1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -198,7 +198,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -static int phytium_dwmac_remove(struct platform_device *pdev) +static void phytium_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -206,8 +206,6 @@ static int phytium_dwmac_remove(struct platform_device *pdev) stmmac_pltfr_remove(pdev); clk_unregister_fixed_rate(plat->stmmac_clk); - - return 0; } #ifdef CONFIG_OF From 24f64599ac09d12f824ae2e2463abb1a6788ae44 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 22 Sep 2024 03:42:30 +0800 Subject: [PATCH 442/521] AOSCOS: net: phytium: Adapt struct kernel_ethtool_ts_info Fixes: 53f8c475850d ("BACKPORT: PHYTIUM: net: phytium: Add support for phytium GMAC") Fixes: 2111375b85ad ("net: Add struct kernel_ethtool_ts_info") Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index cd9d702da113ff..cf6085d2dc04ff 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -176,7 +176,7 @@ static int phytmac_set_ringparam(struct net_device *ndev, } static int phytmac_get_ts_info(struct net_device *ndev, - struct ethtool_ts_info *info) + struct kernel_ethtool_ts_info *info) { struct phytmac *pdata = netdev_priv(ndev); From 6e8bbd03a78d20e96ae0d5fa37fc8e8bf21bf8e8 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 22 Sep 2024 03:57:43 +0800 Subject: [PATCH 443/521] AOSCOS: net: ethernet: phytium: Make phytmac_plat_remove() return void Fixes: 53f8c475850d ("BACKPORT: PHYTIUM: net: phytium: Add support for phytium GMAC") Fixes: 0edb555a65d1 ("platform: Make platform_driver::remove() return void") Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_platform.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index d958ca1f168bb2..3724936cc826e2 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -313,14 +313,12 @@ static int phytmac_plat_probe(struct platform_device *pdev) return ret; } -static int phytmac_plat_remove(struct platform_device *pdev) +static void phytmac_plat_remove(struct platform_device *pdev) { struct phytmac *pdata = platform_get_drvdata(pdev); phytmac_drv_remove(pdata); phytmac_free_pdata(pdata); - - return 0; } static void phytmac_plat_shutdown(struct platform_device *pdev) From 44363fd3e4275cf2ceb5dea631c0c0b94318b99f Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 15 Oct 2025 16:26:32 +0800 Subject: [PATCH 444/521] AOSCOS: net: stmmac: dwmac-phytium: convert to use phy_interface Follow upstream changes post-6.17 and replace mac_interface with phy_interface, the former was removed since commit 6b0ed6a3a89c ("net: stmmac: remove mac_interface"). Fixes: "BACKPORT: PHYTIUM: net/stmmac: Add phytium DWMAC driver support v2" Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index fb6929552380d1..0681225be8c32f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -86,9 +86,9 @@ static int phytium_dwmac_probe(struct platform_device *pdev) if (plat->phy_interface < 0) return plat->phy_interface; - plat->mac_interface = phytium_get_mac_mode(fwnode); - if (plat->mac_interface < 0) - plat->mac_interface = plat->phy_interface; + plat->phy_interface = phytium_get_mac_mode(fwnode); + if (plat->phy_interface < 0) + plat->phy_interface = plat->phy_interface; /* Configure PHY if using device-tree */ if (pdev->dev.of_node) { From e39176855d39f7ca0c1a342cd6346f902f7b36a3 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 5 Nov 2025 10:58:36 +0800 Subject: [PATCH 445/521] AOSCOS: stmmac: stmmac-phytium: fix build against post-6.18 net-next Since commit "net: stmmac: replace has_xxxx with core_type" from post- 6.18 net-next, `has_gmac = [0|1]' has been replaced with `core_type = DWMAC_CORE_GMAC'. This patch series was required by the pre- upstream YT6801 (v2) patchset. Since commit "net: stmmac: remove axi_blen array", the `axi_blen' array is no longer set on the driver level. Drop code to set burst array to fix build since this change. Revise dwmac-phytium.c to adapt to the above changes. Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 0681225be8c32f..18950063f37200 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -106,7 +106,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->phy_addr = -1; plat->clk_csr = -1; - plat->has_gmac = 1; + plat->core_type = DWMAC_CORE_GMAC; plat->enh_desc = 1; plat->bugged_jumbo = 1; plat->pmt = 1; @@ -184,7 +184,6 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->axi->axi_xit_frm = false; plat->axi->axi_wr_osr_lmt = 7; plat->axi->axi_rd_osr_lmt = 7; - plat->axi->axi_blen[0] = 16; memset(&stmmac_res, 0, sizeof(stmmac_res)); stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0); From 445d023a2e76bdd5da377edd5e7933bdc6b02732 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 22 Sep 2024 04:13:54 +0800 Subject: [PATCH 446/521] AOSCOS: arm64: dts: rockchip: disable usb3 on quartz64 USB 3 ports on Quartz64 is of bad quality as it shares lines with SATA. Disable usb3 support to prevent issues. Signed-off-by: Icenowy Zheng Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts index a9021c524afbf8..429ac5492ab4d8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts @@ -795,6 +795,10 @@ /* usb3 controller is muxed with sata1 */ &usb_host1_xhci { + phys = <&usb2phy1_otg>; + phy-names = "usb2-phy"; + extcon = <&usb2phy1>; + maximum-speed = "high-speed"; status = "okay"; }; @@ -808,7 +812,7 @@ }; &usb2phy0_otg { - phy-supply = <&vcc5v0_usb20_otg>; + phy-supply = <&vcc5v0_usb20_host>; status = "okay"; }; From 1c480d6ce5542dd07bd3aaf7f38c697d59221b9b Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 22 Sep 2024 04:18:12 +0800 Subject: [PATCH 447/521] AOSCOS: arm64: drop hisi_ddrc_pmu driver hisi_ddrc_pmu is broken and causes Kunpeng 920-based desktop systems to freeze during boot-up. Disable this driver to workaround this issue. Signed-off-by: Icenowy Zheng Signed-off-by: Kexy Biscuit [Mingcong Bai: Resolved a minor merge conflict since 6.18 in drivers/perf/hisilicon/Makefile.] Signed-off-by: Mingcong Bai --- drivers/perf/hisilicon/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index 186be3d02238b1..1009d4f616aa4e 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \ - hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o hisi_uncore_sllc_pmu.o \ + hisi_uncore_hha_pmu.o hisi_uncore_sllc_pmu.o \ hisi_uncore_pa_pmu.o hisi_uncore_cpa_pmu.o hisi_uncore_uc_pmu.o \ hisi_uncore_noc_pmu.o hisi_uncore_mn_pmu.o From a4e0ddf8510c438ab056b17bbe7cf72591371ceb Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Nov 2025 22:21:05 +0800 Subject: [PATCH 448/521] AOSCOS: loongarch: re-introduce add_numamem_region(), init_node_memblock() This is a partial revert for commit acf5de1b23b0 ("LoongArch: Fix NUMA node parsing with numa_memblks") to keep our BPI/legacy firmware patches working. Further testing and refactoring (and perhaps discussion) pending. Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/numa.c | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/arch/loongarch/kernel/numa.c b/arch/loongarch/kernel/numa.c index 8b89898e20df10..3efd2b4fe37bb8 100644 --- a/arch/loongarch/kernel/numa.c +++ b/arch/loongarch/kernel/numa.c @@ -158,8 +158,75 @@ static void __init node_mem_init(unsigned int node) #ifdef CONFIG_ACPI_NUMA +/* + * add_numamem_region + * + * Add a uasable memory region described by BIOS. The + * routine gets each intersection between BIOS's region + * and node's region, and adds them into node's memblock + * pool. + * + */ +static void __init add_numamem_region(u64 start, u64 end, u32 type) +{ + u32 node = pa_to_nid(start); + u64 size = end - start; + static unsigned long num_physpages; + + if (start >= end) { + pr_debug("Invalid region: %016llx-%016llx\n", start, end); + return; + } + + num_physpages += (size >> PAGE_SHIFT); + pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n", + node, type, start, size); + pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n", + start >> PAGE_SHIFT, end >> PAGE_SHIFT, num_physpages); + memblock_set_node(start, size, &memblock.memory, node); +} + static unsigned long num_physpages; +static void __init init_node_memblock(void) +{ + u32 mem_type; + u64 mem_end, mem_start, mem_size; + efi_memory_desc_t *md; + + /* Parse memory information and activate */ + for_each_efi_memory_desc(md) { + mem_type = md->type; + mem_start = md->phys_addr; + mem_size = md->num_pages << EFI_PAGE_SHIFT; + mem_end = mem_start + mem_size; + + switch (mem_type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_PERSISTENT_MEMORY: + case EFI_CONVENTIONAL_MEMORY: + add_numamem_region(mem_start, mem_end, mem_type); + break; + case EFI_PAL_CODE: + case EFI_UNUSABLE_MEMORY: + case EFI_ACPI_RECLAIM_MEMORY: + add_numamem_region(mem_start, mem_end, mem_type); + fallthrough; + case EFI_RESERVED_TYPE: + case EFI_RUNTIME_SERVICES_CODE: + case EFI_RUNTIME_SERVICES_DATA: + case EFI_MEMORY_MAPPED_IO: + case EFI_MEMORY_MAPPED_IO_PORT_SPACE: + pr_info("Resvd: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n", + mem_type, mem_start, mem_size); + break; + } + } +} + static void __init info_node_memblock(void) { u32 mem_type; From 5f447e8d081d1d762c696b3b9b2900a7b81b8d6a Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Wed, 17 Jul 2024 09:03:32 +0800 Subject: [PATCH 449/521] AOSCOS: loongarch: basic boot support for legacy firmware The addresses passed from legacy firmware in efi system tables, the initrd table, the efi system memory mapping table are virtual addresses. This patch converts all that addresses back to physical addresses, to make sure all the following booting processes can run smoothly like on modern firmwares. The conversion happens unconditionally, since physical addresses passed in by modern firmwares remain unchanged after the conversion. In the EFI Stub, the mapping entries given by GetMemoryMap() on legacy firmwares contain virtual addresses in the phys_addr fields and 0x1xxxx addresses in the virt_addr fields, causing the later call to SetVirtualAddressMap() fails. This patch fixes this by correcting the addresses in the virt_addr fields. This patch detects the existence of the legacy firmware by reading DMW1 CSR, as done in the legacy loongarch GRUB port. Only if legacy firmwares detected, this correction happens. With this patch, the linux kernel is basically able to boot. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/efi.c | 18 ++++++++++++++++++ arch/loongarch/kernel/mem.c | 1 + drivers/firmware/efi/libstub/loongarch.c | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index 69dd83f8082fba..5d818e389b129b 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -99,6 +99,23 @@ static void __init init_primary_display(void) sysfb_primary_display.screen.lfb_size); } +static void __init fix_initrd_table(const efi_config_table_t *config_tables, + int count) +{ + for(int i = 0; i < count; i++) { + if (efi_guidcmp(config_tables[i].guid, + LINUX_EFI_INITRD_MEDIA_GUID) == 0) { + struct linux_efi_initrd *tbl = + early_memremap((u64)config_tables[i].table, sizeof(*tbl)); + if (tbl) { + tbl->base = TO_PHYS(tbl->base); + early_memunmap(tbl, sizeof(*tbl)); + } + break; + } + } +} + void __init efi_init(void) { int size; @@ -124,6 +141,7 @@ void __init efi_init(void) size = sizeof(efi_config_table_t); config_tables = early_memremap(efi_config_table, efi_nr_tables * size); + fix_initrd_table(config_tables, efi_systab->nr_tables); efi_config_parse_tables(config_tables, efi_systab->nr_tables, arch_tables); early_memunmap(config_tables, efi_nr_tables * size); diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c index 8ab1ffedc52c51..d870e9e189736b 100644 --- a/arch/loongarch/kernel/mem.c +++ b/arch/loongarch/kernel/mem.c @@ -19,6 +19,7 @@ void __init memblock_init(void) /* Parse memory information */ for_each_efi_memory_desc(md) { mem_type = md->type; + md->phys_addr = TO_PHYS(md->phys_addr); mem_start = md->phys_addr; mem_size = md->num_pages << EFI_PAGE_SHIFT; diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c index 2b0c87dc990881..11a93c46a34497 100644 --- a/drivers/firmware/efi/libstub/loongarch.c +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -44,6 +44,8 @@ struct exit_boot_struct { int runtime_entry_count; }; +static int is_oldworld = 0; + static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) { struct exit_boot_struct *p = priv; @@ -59,6 +61,15 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) return EFI_SUCCESS; } +static void detect_oldworld(void) +{ + is_oldworld = !!(csr_read64(LOONGARCH_CSR_DMWIN1) & CSR_DMW1_PLV0); + efi_debug("is_oldworld: %d\n", is_oldworld); + if(is_oldworld) { + efi_info("Booting on OldWorld firmware\n"); + } +} + unsigned long __weak kernel_entry_address(unsigned long kernel_addr, efi_loaded_image_t *image) { @@ -74,6 +85,8 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, efi_status_t status; u32 desc_ver; + detect_oldworld(); + status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver); if (status != EFI_SUCCESS) { efi_err("Unable to retrieve UEFI memory map.\n"); @@ -87,11 +100,16 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, if (status != EFI_SUCCESS) return status; + if (is_oldworld) { + goto skip_set_virtual_address_map; + } + /* Install the new virtual address map */ efi_rt_call(set_virtual_address_map, priv.runtime_entry_count * desc_size, desc_size, desc_ver, priv.runtime_map); +skip_set_virtual_address_map: /* Config Direct Mapping */ csr_write(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); csr_write(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); From 229c5c2e4d3eb107caefaf1f82ef789c11c6fa42 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Thu, 18 Jul 2024 18:55:59 +0800 Subject: [PATCH 450/521] AOSCOS: loongarch: parse BPI data and add memory mapping On legacy firmwares, the memory mapping information passed in the EFI system table is not enough to cover all the available memory regions, but only contains the memory region which occupied by the firmware, with the total size of ~1GiB. More information is stored in the BPI data structure. The complete information is combined into from the both sources. This patch addes the mechanism to parse the BPI data structure and extract the memory mapping information from it. This patch also adds the memory regions defined in the BPI to the kernel, letting the kernel to discover all the available memory regions. With this patch, machines with legacy firmware, with the BPI version BPI01001, i.e. the newer version, can boot normally. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit [Fixed minor merge conflicts since v6.18-rc6 in arch/loongarch/kernel/mem.c arch/loongarch/kernel/numa.c] Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/Makefile | 2 + arch/loongarch/kernel/efi.c | 3 + arch/loongarch/kernel/legacy_boot.c | 297 ++++++++++++++++++++++++++++ arch/loongarch/kernel/legacy_boot.h | 18 ++ arch/loongarch/kernel/mem.c | 5 + arch/loongarch/kernel/numa.c | 10 + arch/loongarch/kernel/setup.c | 3 + arch/loongarch/kernel/smp.c | 4 + 8 files changed, 342 insertions(+) create mode 100644 arch/loongarch/kernel/legacy_boot.c create mode 100644 arch/loongarch/kernel/legacy_boot.h diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 001924877772f0..53ad38c9c98f5b 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -80,3 +80,5 @@ obj-$(CONFIG_UPROBES) += uprobes.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o CPPFLAGS_vmlinux.lds := $(KBUILD_CFLAGS) + +obj-y += legacy_boot.o diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index 5d818e389b129b..ad42d6b1f2909c 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -25,6 +25,8 @@ #include #include +#include "legacy_boot.h" + static unsigned long efi_nr_tables; static unsigned long efi_config_table; @@ -35,6 +37,7 @@ static efi_system_table_t *efi_systab; static efi_config_table_type_t arch_tables[] __initdata = { {LINUX_EFI_BOOT_MEMMAP_GUID, &boot_memmap, "MEMMAP" }, {DEVICE_TREE_GUID, &fdt_pointer, "FDTPTR" }, + {LOONGARCH_BPI_GUID, &loongarch_bpi_info.bpi, "BPI" }, {}, }; diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c new file mode 100644 index 00000000000000..30c01c8b22a0a5 --- /dev/null +++ b/arch/loongarch/kernel/legacy_boot.c @@ -0,0 +1,297 @@ +#include +#include + +#include + +#include "legacy_boot.h" + +#define LOONGARCH_BOOT_MEM_MAP_MAX 128 + +enum bpi_vers { + BPI_VERSION_NONE = 0, + BPI_VERSION_V1 = 1000, + BPI_VERSION_V2 = 1001, +}; + +struct loongarch_bpi_ext_hdr { + u64 signature; + u32 length; + u8 revision; + u8 checksum; + u64 next; +} __packed; + +struct loongarch_bpi_hdr { + u64 signature; + u64 systemtable; + u64 extlist; + u64 flags; +} __packed; + +enum bpi_mem_type { + ADDRESS_TYPE_SYSRAM = 1, + ADDRESS_TYPE_RESERVED = 2, + ADDRESS_TYPE_ACPI = 3, + ADDRESS_TYPE_NVS = 4, + ADDRESS_TYPE_PMEM = 5, + ADDRESS_TYPE_MAX, +}; + +struct loongarch_bpi_mem_map { + struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ + u8 map_count; + struct loongarch_bpi_mem_map_entry { + u32 mem_type; + u64 mem_start; + u64 mem_size; + } __packed map[]; +} __packed; + +struct loongarch_bpi_info __initdata loongarch_bpi_info = { + .bpi = EFI_INVALID_TABLE_ADDR, +}; + +static enum bpi_vers __initdata bpi_version = BPI_VERSION_NONE; +static u64 __initdata bpi_flags = 0; + +static int have_bpi = 0; + +static __initdata struct { + size_t map_count; + struct loongarch_bpi_memmap{ + enum bpi_mem_type type; + unsigned long mem_start; + size_t mem_size; + } map[LOONGARCH_BOOT_MEM_MAP_MAX]; +} bpi_meminfo = {0}; + +static __initdata struct { + unsigned long addr; + size_t size; +} bpi_memmap = { + .addr = EFI_INVALID_TABLE_ADDR, + .size = 0, +}; + +static const __initconst struct bpi_extlist_desc { + union { + u64 signature; + char chars[8]; + }; + unsigned long *ptr; + size_t *length; +} bpi_extlist[] = { + { .chars = "MEM", &bpi_memmap.addr, &bpi_memmap.size }, + { .signature = 0, NULL, NULL }, +}; + + +static void __init parse_bpi_ext_list(unsigned long exthdr) +{ + unsigned long cur_hdr = exthdr; + while(cur_hdr){ + struct loongarch_bpi_ext_hdr *hdr = early_memremap_ro(cur_hdr, sizeof(*hdr)); + if(!hdr) { + break; + } + u64 sig = hdr->signature; + u32 len = hdr->length; + u8 rev = hdr->revision; + unsigned long next = hdr->next; + early_memunmap(hdr, sizeof(*hdr)); + + pr_info("BPI: ext hdr: %.8s, rev %u\n", (const char *)&sig, rev); + + for(const struct bpi_extlist_desc *desc = bpi_extlist; desc->signature; desc++) { + if(sig == desc->signature) { + *(desc->ptr) = cur_hdr; + *(desc->length) = len; + } + } + cur_hdr = next; + } +} + + +static u8 __init ext_listhdr_checksum(void *buffer, size_t length) +{ + u8 sum = 0; + u8 *end = buffer + length; + u8 *buf = buffer; + + while (buf < end) + sum = (u8)(sum + *(buf++)); + + return sum; +} + +static void __init parse_bpi_mem_map(void) +{ + if(bpi_memmap.addr == EFI_INVALID_TABLE_ADDR) { + return; + } + if(bpi_memmap.size < sizeof(struct loongarch_bpi_mem_map)) { + pr_err("BPI: invalid memmap size %ld\n", bpi_memmap.size); + return; + } + struct loongarch_bpi_mem_map *memmap = early_memremap_ro(bpi_memmap.addr, bpi_memmap.size); + if(!memmap) { + return; + } + + u8 checksum = ext_listhdr_checksum(memmap, bpi_memmap.size); + if (checksum != 0) { + pr_err("BPI: memmap checksum mismatch\n"); + goto err_out; + } + + size_t map_count = memmap->map_count; + if (map_count > LOONGARCH_BOOT_MEM_MAP_MAX) { + pr_err("BPI: too many memmap entries\n"); + goto err_out; + } + if (map_count * sizeof(memmap->map[0]) + sizeof(*memmap) > bpi_memmap.size) { + pr_err("BPI: invalid memmap size %ld, not enough to hold %ld entries\n", bpi_memmap.size, map_count); + goto err_out; + } + for(int i = 0; i < map_count; i++) { + bpi_meminfo.map[i].type = memmap->map[i].mem_type; + bpi_meminfo.map[i].mem_start = memmap->map[i].mem_start; + bpi_meminfo.map[i].mem_size = memmap->map[i].mem_size; + + static const char * const __initconst mem_type_str[] = { + NULL, + [ADDRESS_TYPE_SYSRAM] = "SYSRAM", + [ADDRESS_TYPE_RESERVED] = "RESERVED", + [ADDRESS_TYPE_ACPI] = "ACPI", + [ADDRESS_TYPE_NVS] = "NVS", + [ADDRESS_TYPE_PMEM] = "PMEM", + }; + if(bpi_meminfo.map[i].type >= ADDRESS_TYPE_MAX || bpi_meminfo.map[i].type == 0) { + pr_info("BPI: memmap type unknown(%d), start 0x%lx, size 0x%lx\n", bpi_meminfo.map[i].type, bpi_meminfo.map[i].mem_start, bpi_meminfo.map[i].mem_size); + }else{ + pr_info("BPI: memmap type %s, start 0x%lx, size 0x%lx\n", mem_type_str[bpi_meminfo.map[i].type], bpi_meminfo.map[i].mem_start, bpi_meminfo.map[i].mem_size); + } + } + bpi_meminfo.map_count = map_count; + +err_out: + early_memunmap(memmap, bpi_memmap.size); +} + +static int __init bpi_parse_signature (u64 signature) +{ + union { + u64 signature; + char chars[9]; + } sig; + sig.signature = signature; + sig.chars[8] = '\0'; + + if (!(sig.chars[0] == 'B' && sig.chars[1] == 'P' && sig.chars[2] == 'I')) { + pr_err("BPI: invalid signature\n"); + return -EINVAL; + } + int version; + int rc = kstrtoint(&sig.chars[3], 10, &version); + if(rc != 0 || version == BPI_VERSION_NONE) { + pr_err("BPI: invalid version\n"); + return -EINVAL; + } + bpi_version = version; + pr_info("BPI: version %d\n", bpi_version); + return 0; +} + +void __init bpi_init(void) +{ + if (loongarch_bpi_info.bpi == EFI_INVALID_TABLE_ADDR) { + return; + } + struct loongarch_bpi_hdr *tbl; + + tbl = early_memremap_ro(loongarch_bpi_info.bpi, sizeof(*tbl)); + if (tbl) { + int rc = bpi_parse_signature(tbl->signature); + if (rc != 0) { + goto err_out; + } + have_bpi = 1; + if (bpi_version >= BPI_VERSION_V2) { + bpi_flags = tbl->flags; + pr_info("BPI: flags 0x%llx\n", bpi_flags); + } + unsigned long bpi_extlist = tbl->extlist; + parse_bpi_ext_list(bpi_extlist); + parse_bpi_mem_map(); +err_out: + early_memunmap(tbl, sizeof(*tbl)); + } +} + +void __init bpi_memblock_init(unsigned long *p_max_low_pfn) +{ + for(int i = 0; i < bpi_meminfo.map_count; i++) { + unsigned long start = bpi_meminfo.map[i].mem_start; + size_t size = bpi_meminfo.map[i].mem_size; + unsigned long end = start + size; + + switch(bpi_meminfo.map[i].type) { + case ADDRESS_TYPE_SYSRAM: + // EFI_CONVENTIONAL_MEMORY + case ADDRESS_TYPE_PMEM: + // EFI_PERSISTENT_MEMORY + memblock_add(start, size); + if (*p_max_low_pfn < (end >> PAGE_SHIFT)) + *p_max_low_pfn = end >> PAGE_SHIFT; + break; + case ADDRESS_TYPE_ACPI: + // EFI_ACPI_RECLAIM_MEMORY + memblock_add(start, size); + fallthrough; + case ADDRESS_TYPE_RESERVED: + // EFI_RUNTIME_SERVICES_DATA or + // EFI_RUNTIME_SERVICES_CODE or + // EFI_RESERVED_MEMORY_TYPE + memblock_reserve(start, size); + break; + default: + break; + } + } +} + +void __init bpi_init_node_memblock(void (*p_add_numamem_region)(u64 start, u64 end, u32 type)) +{ + for(int i = 0; i < bpi_meminfo.map_count; i++) { + u64 mem_start = bpi_meminfo.map[i].mem_start; + u64 mem_size = bpi_meminfo.map[i].mem_size; + u64 mem_end = mem_start + mem_size; + u32 mem_type; + + switch(bpi_meminfo.map[i].type) { + case ADDRESS_TYPE_SYSRAM: + mem_type = EFI_CONVENTIONAL_MEMORY; + p_add_numamem_region(mem_start, mem_end, mem_type); + break; + case ADDRESS_TYPE_PMEM: + mem_type = EFI_PERSISTENT_MEMORY; + p_add_numamem_region(mem_start, mem_end, mem_type); + break; + case ADDRESS_TYPE_ACPI: + mem_type = EFI_ACPI_RECLAIM_MEMORY; + p_add_numamem_region(mem_start, mem_end, mem_type); + fallthrough; + case ADDRESS_TYPE_RESERVED: + pr_info("Resvd: mem_type:BPI_%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n", + bpi_meminfo.map[i].type, mem_start, mem_size); + break; + default: + break; + } + } +} + +int loongarch_have_legacy_bpi (void){ + return have_bpi; +} diff --git a/arch/loongarch/kernel/legacy_boot.h b/arch/loongarch/kernel/legacy_boot.h new file mode 100644 index 00000000000000..6d3a36ebaab717 --- /dev/null +++ b/arch/loongarch/kernel/legacy_boot.h @@ -0,0 +1,18 @@ +#ifndef __LEGACY_BOOT_H_ +#define __LEGACY_BOOT_H_ + +#include + +#define LOONGARCH_BPI_GUID EFI_GUID(0x4660f721, 0x2ec5, 0x416a, 0x89, 0x9a, 0x43, 0x18, 0x02, 0x50, 0xa0, 0xc9) + +struct loongarch_bpi_info { + unsigned long bpi; +}; + +extern struct loongarch_bpi_info loongarch_bpi_info; +extern void bpi_init(void); +extern void bpi_memblock_init(unsigned long *p_max_low_pfn); +extern void bpi_init_node_memblock(void (*p_add_numamem_region)(u64 start, u64 end, u32 type)); +extern int loongarch_have_legacy_bpi(void); + +#endif /* __LEGACY_BOOT_H_ */ diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c index d870e9e189736b..86156ffdee5dc5 100644 --- a/arch/loongarch/kernel/mem.c +++ b/arch/loongarch/kernel/mem.c @@ -10,6 +10,8 @@ #include #include +#include "legacy_boot.h" + void __init memblock_init(void) { u32 mem_type; @@ -49,6 +51,9 @@ void __init memblock_init(void) max_pfn = PFN_DOWN(memblock_end_of_DRAM()); max_low_pfn = min(PFN_DOWN(HIGHMEM_START), max_pfn); + + bpi_memblock_init(&max_low_pfn); + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); /* Reserve the first 2MB */ diff --git a/arch/loongarch/kernel/numa.c b/arch/loongarch/kernel/numa.c index 3efd2b4fe37bb8..38a10744e9a528 100644 --- a/arch/loongarch/kernel/numa.c +++ b/arch/loongarch/kernel/numa.c @@ -27,6 +27,8 @@ #include #include +#include "legacy_boot.h" + int numa_off; cpumask_t cpus_on_node[MAX_NUMNODES]; cpumask_t phys_cpus_on_node[MAX_NUMNODES]; @@ -308,6 +310,14 @@ int __init init_numa_memory(void) return ret; info_node_memblock(); + + node_possible_map = numa_nodes_parsed; + if (WARN_ON(nodes_empty(node_possible_map))) + return -EINVAL; + + init_node_memblock(); + bpi_init_node_memblock(add_numamem_region); + if (!memblock_validate_numa_coverage(SZ_1M)) return -EINVAL; diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index bfc6f274e6e28c..0f21c718edfea9 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -49,6 +49,8 @@ #include #include +#include "legacy_boot.h" + #define SMBIOS_BIOSSIZE_OFFSET 0x09 #define SMBIOS_BIOSEXTERN_OFFSET 0x13 #define SMBIOS_FREQLOW_OFFSET 0x16 @@ -599,6 +601,7 @@ void __init setup_arch(char **cmdline_p) init_environ(); efi_init(); fdt_setup(); + bpi_init(); memblock_init(); pagetable_init(); bootcmdline_init(cmdline_p); diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 401fc236aac27b..adcf53684eccf5 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -36,6 +36,8 @@ #include #include +#include "legacy_boot.h" + int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ EXPORT_SYMBOL(__cpu_number_map); @@ -400,6 +402,8 @@ void loongson_boot_secondary(int cpu, struct task_struct *idle) pr_info("Booting CPU#%d...\n", cpu); entry = __pa_symbol((unsigned long)&smpboot_entry); + if (loongarch_have_legacy_bpi()) + entry = (unsigned long)&smpboot_entry; cpuboot_data.stack = (unsigned long)__KSTK_TOS(idle); cpuboot_data.thread_info = (unsigned long)task_thread_info(idle); From 48aaf692269ed82f8c27e75c09f159f397823a19 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Sat, 20 Jul 2024 06:32:15 +0800 Subject: [PATCH 451/521] AOSCOS: loongarch: add MADT ACPI table conversion On machines with legacy firmware with BPI version BPI01000, i.e. the older version, the content of the MADT ACPI table is not following the later finalized ACPI standard. A conversion of the content is added by this patch. Also the patch generates and adds a MCFG table accordingly. The above behavior is only enabled when BPI data and the BPI01000 version is detected. Signed-off-by: Miao Wang Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/include/asm/acpi.h | 5 + arch/loongarch/kernel/legacy_boot.c | 304 ++++++++++++++++++++++++++++ drivers/acpi/tables.c | 2 + include/linux/acpi.h | 10 + 4 files changed, 321 insertions(+) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index c05168aedcaa2d..519d6401ae21e0 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -42,6 +42,11 @@ extern void acpi_add_early_pio(void); extern void acpi_remove_early_pio(void); extern int __init parse_acpi_topology(void); +#define ACPI_HAVE_ARCH_TABLE_OVERRIDE +extern void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table); +#define ACPI_HAVE_ARCH_TABLE_INIT_COMPLETE +extern void acpi_arch_table_init_complete(void); + #endif /* !CONFIG_ACPI */ #define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 30c01c8b22a0a5..841d8db85a3cb1 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -1,7 +1,13 @@ #include #include +#include +#include +#include #include +#include +#include +#include #include "legacy_boot.h" @@ -37,6 +43,9 @@ enum bpi_mem_type { ADDRESS_TYPE_MAX, }; +#define MSI_MSG_ADDRESS 0x2FF00000 +#define MSI_MSG_DEFAULT_COUNT 0xC0 + struct loongarch_bpi_mem_map { struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ u8 map_count; @@ -203,6 +212,9 @@ static int __init bpi_parse_signature (u64 signature) return 0; } +static void (*p_init_acpi_arch_os_table_override)(struct acpi_table_header *, struct acpi_table_header **) = NULL; +static void init_acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table); + void __init bpi_init(void) { if (loongarch_bpi_info.bpi == EFI_INVALID_TABLE_ADDR) { @@ -220,6 +232,8 @@ void __init bpi_init(void) if (bpi_version >= BPI_VERSION_V2) { bpi_flags = tbl->flags; pr_info("BPI: flags 0x%llx\n", bpi_flags); + } else { + p_init_acpi_arch_os_table_override = init_acpi_arch_os_table_override; } unsigned long bpi_extlist = tbl->extlist; parse_bpi_ext_list(bpi_extlist); @@ -292,6 +306,296 @@ void __init bpi_init_node_memblock(void (*p_add_numamem_region)(u64 start, u64 e } } +static u8 new_mcfg_buf [ 0 + + sizeof(struct acpi_table_mcfg) + + MAX_IO_PICS * sizeof(struct acpi_mcfg_allocation) +]; +static int __initdata new_mcfg_available = 0; + +void __init acpi_arch_table_init_complete(void){ + if (!new_mcfg_available){ + return; + } + pr_info("BPI: installing new MCFG table\n"); + acpi_install_table((struct acpi_table_header *) new_mcfg_buf); +} + +static void __init init_acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ + static int __initdata madt_table_installed = 0; + static u8 new_madt_buf[ 0 + + sizeof(struct acpi_table_madt) + + sizeof(struct acpi_madt_lio_pic) + + sizeof(struct acpi_madt_core_pic) * MAX_CORE_PIC + + MAX_IO_PICS * ( 0 + + sizeof(struct acpi_madt_eio_pic) + + sizeof(struct acpi_madt_msi_pic) + + sizeof(struct acpi_madt_bio_pic) + ) + ]; + if (madt_table_installed){ + return; + } + if (bpi_version == BPI_VERSION_NONE) { + return; + } + if (bpi_version > BPI_VERSION_V1) { + return; + } + if (strncmp(existing_table->signature, ACPI_SIG_MADT, 4) != 0) { + return; + } + pr_info("BPI: replacing MADT table\n"); + struct acpi_table_madt *madt = (struct acpi_table_madt *)existing_table; + if (madt->header.length < sizeof(struct acpi_table_madt)) { + pr_warn("BPI: MADT table length %u is too small\n", madt->header.length); + return; + } + void *madt_end = (void *)madt + madt->header.length; + struct acpi_subtable_header *entry = (struct acpi_subtable_header *)(madt + 1); + int entry_count = 0; + int local_apic_count = 0; + int io_apic_count = 0; + u64 node_map = 0; + while((void *)entry < madt_end) { + unsigned int ent_len = entry->length; + if (ent_len < sizeof(struct acpi_subtable_header)) { + pr_warn("BPI: MADT entry %d length %u is too small\n", entry_count, ent_len); + return; + } + if((void *)entry + ent_len > madt_end) { + pr_warn("BPI: MADT entry %d length overflow\n", entry_count); + return; + } + switch(entry->type) { + case ACPI_MADT_TYPE_LOCAL_APIC: + local_apic_count++; + struct acpi_madt_local_apic *lapic = (struct acpi_madt_local_apic *)entry; + node_map |= 1 << (lapic->id / CORES_PER_EIO_NODE); + break; + case ACPI_MADT_TYPE_IO_APIC: + io_apic_count++; + break; + } + acpi_table_print_madt_entry(entry); + + entry = (struct acpi_subtable_header *)((void *)entry + ent_len); + } + + if (local_apic_count == 0) { + pr_warn("BPI: MADT has no local APIC entries\n"); + return; + } + if (local_apic_count > MAX_CORE_PIC) { + pr_warn("BPI: MADT has too many local APIC entries\n"); + return; + } + if (io_apic_count == 0) { + pr_warn("BPI: MADT has no IO APIC entries\n"); + return; + } + if (io_apic_count > MAX_IO_PICS) { + pr_warn("BPI: MADT has too many IO APIC entries\n"); + return; + } + size_t new_madt_size = sizeof(struct acpi_table_madt); + size_t new_mcfg_size = sizeof(struct acpi_table_mcfg); + new_madt_size += sizeof(struct acpi_madt_lio_pic); + new_madt_size += sizeof(struct acpi_madt_core_pic) * local_apic_count; + if (cpu_has_extioi) { + pr_info("BPI: Using EIOINTC interrupt mode\n"); + new_madt_size += io_apic_count * ( 0 + + sizeof(struct acpi_madt_eio_pic) + + sizeof(struct acpi_madt_msi_pic) + + sizeof(struct acpi_madt_bio_pic) + ); + new_madt_size += sizeof(struct acpi_madt_lpc_pic); + new_mcfg_size += io_apic_count * sizeof(struct acpi_mcfg_allocation); + } else { + pr_info("BPI: Using HTVECINTC interrupt mode\n"); + new_madt_size += 0; + new_mcfg_size += sizeof(struct acpi_mcfg_allocation); + } + + if (new_madt_size > sizeof(new_madt_buf)) { + pr_warn("BPI: new madt will be too large"); + return; + } + if (new_mcfg_size > sizeof(new_mcfg_buf)) { + pr_warn("BPI: new mcfg will be too large"); + return; + } + madt_table_installed = 1; + new_mcfg_available = 1; + + struct acpi_table_madt *new_madt = (struct acpi_table_madt *)new_madt_buf; + + new_madt->header = madt->header; + new_madt->header.length = new_madt_size; + new_madt->header.checksum = 0; + new_madt->header.asl_compiler_id[0] = 'B'; + new_madt->header.asl_compiler_id[1] = 'P'; + new_madt->header.asl_compiler_id[2] = 'I'; + new_madt->address = madt->address; + new_madt->flags = madt->flags; + + struct acpi_table_mcfg *new_mcfg = (struct acpi_table_mcfg *)new_mcfg_buf; + + memcpy(new_mcfg->header.signature, ACPI_SIG_MCFG, sizeof(new_mcfg->header.signature)); + new_mcfg->header.length = new_mcfg_size; + new_mcfg->header.revision = 1; + new_mcfg->header.checksum = 0; + memcpy(new_mcfg->header.oem_id, madt->header.oem_id, sizeof(new_mcfg->header.oem_id)); + memcpy(new_mcfg->header.oem_table_id, madt->header.oem_table_id, sizeof(new_mcfg->header.oem_table_id)); + new_mcfg->header.oem_revision = madt->header.oem_revision; + memcpy(new_mcfg->header.asl_compiler_id, "BPI", sizeof(new_mcfg->header.asl_compiler_id)); + new_mcfg->header.asl_compiler_revision = 1; + memset(new_mcfg->reserved, 0, sizeof(new_mcfg->reserved)); + + struct acpi_mcfg_allocation *mcfg_entry = (struct acpi_mcfg_allocation *)(new_mcfg + 1); + + static struct acpi_madt_core_pic __initdata core_pics[MAX_CORE_PIC]; + static struct acpi_madt_eio_pic __initdata eio_pics[MAX_IO_PICS]; + static struct acpi_madt_msi_pic __initdata msi_pics[MAX_IO_PICS]; + static struct acpi_madt_bio_pic __initdata bio_pics[MAX_IO_PICS]; + + entry = (struct acpi_subtable_header *)(madt + 1); + int core_idx = 0; + int eio_idx = 0; + while((void *)entry < madt_end) { + unsigned int ent_len = entry->length; + if(entry->type == ACPI_MADT_TYPE_LOCAL_APIC) { + struct acpi_madt_local_apic *lapic = (struct acpi_madt_local_apic *)entry; + + if(core_idx >= local_apic_count){ + panic("BPI: MADT local APIC entries are more than expected\n"); + } + + core_pics[core_idx].header.type = ACPI_MADT_TYPE_CORE_PIC; + core_pics[core_idx].header.length = sizeof(core_pics[0]); + core_pics[core_idx].version = ACPI_MADT_CORE_PIC_VERSION_V1; + core_pics[core_idx].processor_id = lapic->processor_id; + core_pics[core_idx].core_id = lapic->id; + core_pics[core_idx].flags = lapic->lapic_flags; + + core_idx++; + }else if(entry->type == ACPI_MADT_TYPE_IO_APIC) { + struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry; + + if(eio_idx >= io_apic_count){ + panic("BPI: MADT IO APIC entries are more than expected\n"); + } + + eio_pics[eio_idx].header.type = ACPI_MADT_TYPE_EIO_PIC; + eio_pics[eio_idx].header.length = sizeof(eio_pics[0]); + eio_pics[eio_idx].version = ACPI_MADT_EIO_PIC_VERSION_V1; + eio_pics[eio_idx].cascade = 3 + eio_idx; + eio_pics[eio_idx].node = ioapic->id; + if(eio_idx == 0){ + eio_pics[eio_idx].node_map = node_map; + }else{ + eio_pics[0].node_map = node_map & 0x0f0f0f0f0f0f0f0full; + eio_pics[eio_idx].node_map = node_map & 0xf0f0f0f0f0f0f0f0ull; + } + + unsigned long addr = ioapic->address; + if(eio_idx > 0){ + addr |= nid_to_addrbase(ioapic->id) | HT1LO_OFFSET; + } + + msi_pics[eio_idx].header.type = ACPI_MADT_TYPE_MSI_PIC; + msi_pics[eio_idx].header.length = sizeof(msi_pics[0]); + msi_pics[eio_idx].version = ACPI_MADT_MSI_PIC_VERSION_V1; + msi_pics[eio_idx].msg_address = MSI_MSG_ADDRESS; + pr_info("BPI: will read MSI start addr for node %d from 0x%lx\n", ioapic->id, addr); + msi_pics[eio_idx].start = (((unsigned long)ls7a_readq(addr) >> 48) & 0xff) + 1; + pr_info("BPI: done read MSI start addr for node %d from 0x%lx\n", ioapic->id, addr); + msi_pics[eio_idx].count = MSI_MSG_DEFAULT_COUNT; + + bio_pics[eio_idx].header.type = ACPI_MADT_TYPE_BIO_PIC; + bio_pics[eio_idx].header.length = sizeof(bio_pics[0]); + bio_pics[eio_idx].version = ACPI_MADT_BIO_PIC_VERSION_V1; + bio_pics[eio_idx].address = addr; + bio_pics[eio_idx].size = 0x1000; + bio_pics[eio_idx].id = ioapic->id; + bio_pics[eio_idx].gsi_base = ioapic->global_irq_base; + + mcfg_entry->address = mcfg_addr_init(ioapic->id); + mcfg_entry->pci_segment = eio_idx; + mcfg_entry->start_bus_number = 0; + mcfg_entry->end_bus_number = 0xFF; // Who knows? + mcfg_entry->reserved = 0; + mcfg_entry++; + + eio_idx++; + } + entry = (struct acpi_subtable_header *)((void *)entry + ent_len); + } + if(eio_idx != io_apic_count || core_idx != local_apic_count) { + panic("BPI: MADT entries are less than expected\n"); + } + + // 1. Core APIC 0x11 + struct acpi_subtable_header *new_entry = (struct acpi_subtable_header *)(new_madt + 1); + + memcpy(new_entry, core_pics, local_apic_count * sizeof(core_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + local_apic_count * sizeof(core_pics[0])); + + // 2. LIO PIC 0x12 + { + struct acpi_madt_lio_pic *lio_pic = (struct acpi_madt_lio_pic *)new_entry; + lio_pic->header.type = ACPI_MADT_TYPE_LIO_PIC; + lio_pic->header.length = sizeof(*lio_pic); + lio_pic->version = ACPI_MADT_LIO_PIC_VERSION_V1; + lio_pic->address = LOONGSON_REG_BASE + 0x1400; + lio_pic->size = 256; + lio_pic->cascade[0] = 2; + lio_pic->cascade[1] = 3; + lio_pic->cascade_map[0] = 0x00FFFFFF; + lio_pic->cascade_map[1] = 0xFF000000; + new_entry = (struct acpi_subtable_header *)((void *)new_entry + sizeof(*lio_pic)); + } + // 3. HT PIC 0x13 + if (!cpu_has_extioi) { + // FIX ME: Unsupported + } else { + // 4. EIO PIC 0x14 + memcpy(new_entry, eio_pics, io_apic_count * sizeof(eio_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + io_apic_count * sizeof(eio_pics[0])); + // 5. MSI PIC 0x15 + memcpy(new_entry, msi_pics, io_apic_count * sizeof(msi_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + io_apic_count * sizeof(msi_pics[0])); + // 6. BIO PIC 0x16 + memcpy(new_entry, bio_pics, io_apic_count * sizeof(bio_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + io_apic_count * sizeof(bio_pics[0])); + // 7. LPC PIC 0x17 + { + struct acpi_madt_lpc_pic *lpc_pic = (struct acpi_madt_lpc_pic *)new_entry; + lpc_pic->header.type = ACPI_MADT_TYPE_LPC_PIC; + lpc_pic->header.length = sizeof(*lpc_pic); + lpc_pic->version = ACPI_MADT_LPC_PIC_VERSION_V1; + lpc_pic->address = LS7A_LPC_REG_BASE; + lpc_pic->size = SZ_4K; + lpc_pic->cascade = 19; + new_entry = (struct acpi_subtable_header *)((void *)new_entry + sizeof(*lpc_pic)); + } + } + if((void *)new_entry != (void *)new_madt + new_madt_size) { + panic("BPI: missing bytes while constructing new MADT\n"); + } + if((void *)mcfg_entry != (void *)new_mcfg + new_mcfg_size) { + panic("BPI: missing bytes while constructing new MCFG\n"); + } + new_madt->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_madt, new_madt_size); + new_mcfg->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_mcfg, new_mcfg_size); + *new_table = (struct acpi_table_header *)new_madt; +} + +void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ + if(p_init_acpi_arch_os_table_override && system_state == SYSTEM_BOOTING) { + p_init_acpi_arch_os_table_override(existing_table, new_table); + } +} + int loongarch_have_legacy_bpi (void){ return have_bpi; } diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index eb93c060426ffd..50ef860659dfa4 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -694,6 +694,7 @@ acpi_status acpi_os_table_override(struct acpi_table_header *existing_table, *new_table = (struct acpi_table_header *)&dsdt_amlcode; } #endif + acpi_arch_os_table_override(existing_table, new_table); if (*new_table != NULL) acpi_table_taint(existing_table); return AE_OK; @@ -753,6 +754,7 @@ void __init acpi_table_init_complete(void) { acpi_table_initrd_scan(); check_multiple_madt(); + acpi_arch_table_init_complete(); } int __init acpi_table_init(void) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 67effb91fa9837..613d4b49ae3834 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -810,6 +810,16 @@ int acpi_mrrm_max_mem_region(void); extern bool cmos_rtc_platform_device_present; +#ifndef ACPI_HAVE_ARCH_TABLE_OVERRIDE +static inline void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ +} +#endif +#ifndef ACPI_HAVE_ARCH_TABLE_INIT_COMPLETE +static inline void acpi_arch_table_init_complete(void) +{ +} +#endif + #else /* !CONFIG_ACPI */ #define acpi_disabled 1 From 01e6357b14a9384758ef8197d38bad15f7c0718a Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Wed, 24 Jul 2024 09:06:27 +0800 Subject: [PATCH 452/521] AOSCOS: loongarch: correct missing offset of PCI root controller in DSDT table On machines with legacy firmware with BPI version BPI01000, the PCI root controller information in the DSDT table lacks AddressTranslation in its WordIO resource defination, causing the failure of the registration for the LIO address space. This patch corrects this issue when the given offset is zero for the PCI root controller. This patch also fixes the lack of the leading 16K, i.e. ISA_IOSIZE, in the defination of WordIO resource. This is because that address range is registered unconditionally on the legacy loongarch linux port. This patch also fixes the start addresses or end addresses of WordIO resource not aligned to the page size by rouding the addresses up to the nearest page starting. The above behavior is only enabled when BPI data and the BPI01000 version is detected. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/include/asm/acpi.h | 2 ++ arch/loongarch/kernel/legacy_boot.c | 23 ++++++++++++++++++++++- drivers/acpi/pci_root.c | 1 + include/linux/pci-acpi.h | 4 ++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index 519d6401ae21e0..6a7299be0878f8 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -46,6 +46,8 @@ extern int __init parse_acpi_topology(void); extern void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table); #define ACPI_HAVE_ARCH_TABLE_INIT_COMPLETE extern void acpi_arch_table_init_complete(void); +#define ACPI_HAVE_ARCH_PCI_ROOT_RES_FILTER +extern void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry); #endif /* !CONFIG_ACPI */ diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 841d8db85a3cb1..7043bfd0399d66 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -60,7 +60,7 @@ struct loongarch_bpi_info __initdata loongarch_bpi_info = { .bpi = EFI_INVALID_TABLE_ADDR, }; -static enum bpi_vers __initdata bpi_version = BPI_VERSION_NONE; +static enum bpi_vers bpi_version = BPI_VERSION_NONE; static u64 __initdata bpi_flags = 0; static int have_bpi = 0; @@ -596,6 +596,27 @@ void acpi_arch_os_table_override (struct acpi_table_header *existing_table, stru } } +void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry) +{ + if (bpi_version == BPI_VERSION_NONE) { + return; + } + if (bpi_version > BPI_VERSION_V1) { + return; + } + + if (entry->res->flags & IORESOURCE_IO) { + if (entry->offset == 0) { + if (entry->res->start == ISA_IOSIZE) { + entry->res->start = 0; + } + entry->offset = LOONGSON_LIO_BASE; + entry->res->start = LOONGSON_LIO_BASE + PFN_ALIGN(entry->res->start); + entry->res->end = LOONGSON_LIO_BASE + PFN_ALIGN(entry->res->end + 1) - 1; + } + } +} + int loongarch_have_legacy_bpi (void){ return have_bpi; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index de9d14fa9528ad..4fca040a52350f 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -912,6 +912,7 @@ 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) { + acpi_arch_pci_probe_root_dev_filter(entry); if (entry->res->flags & IORESOURCE_IO) acpi_pci_root_remap_iospace(device, entry); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index c0c54baadf04ed..99fc8051585b09 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -134,6 +134,10 @@ static inline void pci_acpi_remove_edr_notifier(struct pci_dev *pdev) { } int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *)); void pci_acpi_clear_companion_lookup_hook(void); +#ifndef ACPI_HAVE_ARCH_PCI_ROOT_RES_FILTER +static inline void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry) { } +#endif + #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } From c8b269867eb983bb4f7cedf3e27106318fe6067f Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Thu, 25 Jul 2024 16:09:19 +0800 Subject: [PATCH 453/521] AOSCOS: loongarch: fix missing dependency info in DSDT On machines with legacy firmware with BPI01000 version, the devices on the LIO bus lackes the dependency on the PCI root controller, causing the memory-mapped address of the legacy IO ports read before the setup of the mapping, resulting in kernel panic. Such DSDT can work on the legacy loongarch linux port because the leading 16K is unconditionally registered, before the enumeration of the devices in the DSDT table. This patch addes such dependency info, to order the initialization of the devices on the LIO bus after the initialization of the PCI root controller, fixing this problem. However, the addition should be done on each possible LIO device, and currently the patch only includes the legacy EC device on some laptops located at the path \_SB.PCI0.LPC.EC. Thus, this patch will be improved to include more devices. The above behavior is only enabled when BPI data and the BPI01000 version is detected. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/include/asm/acpi.h | 2 ++ arch/loongarch/kernel/legacy_boot.c | 47 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index 6a7299be0878f8..7c42fc15720dae 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -48,6 +48,8 @@ extern void acpi_arch_os_table_override (struct acpi_table_header *existing_tabl extern void acpi_arch_table_init_complete(void); #define ACPI_HAVE_ARCH_PCI_ROOT_RES_FILTER extern void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry); +#define ACPI_HAVE_ARCH_INIT +extern void acpi_arch_init(void); #endif /* !CONFIG_ACPI */ diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 7043bfd0399d66..3ec69ffd8b9420 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -617,6 +617,53 @@ void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry) } } +static __initconst const struct { + struct acpi_table_header header; + unsigned char code []; +} __packed dsdt_add_aml_code = { + .header = { + .signature = ACPI_SIG_DSDT + }, + .code = { + 0x14,0x21,0x5C,0x2F, /* 00000020 " .!\/" */ + 0x05,0x5F,0x53,0x42,0x5F,0x50,0x43,0x49, /* 00000028 "._SB_PCI" */ + 0x30,0x4C,0x50,0x43,0x5F,0x45,0x43,0x5F, /* 00000030 "0LPC_EC_" */ + 0x5F,0x5F,0x44,0x45,0x50,0x08,0xA4,0x12, /* 00000038 "__DEP..." */ + 0x06,0x01,0x50,0x43,0x49,0x30 /* 00000040 "..PCI0" */ + }, +}; + +void __init acpi_arch_init (){ + if (bpi_version == BPI_VERSION_NONE) { + return; + } + if (bpi_version > BPI_VERSION_V1) { + return; + } + pr_info("BPI: Trying to patch DSDT\n"); + + acpi_status status; + acpi_handle handle; + + status = acpi_get_handle(NULL, "\\_SB.PCI0.LPC.EC", &handle); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + pr_info("BPI: Unable to find EC device: %s\n", acpi_format_exception(status)); + } + return; + } + if (acpi_has_method(handle, "_DEP")) { + return; + } + + status = acpi_install_method((u8 *)&dsdt_add_aml_code); + if (ACPI_FAILURE(status)) { + pr_info("BPI: Unable to patch DSDT(0x%x)\n", status); + return; + } + acpi_handle_info(handle, "BPI: Patched DSDT\n"); +} + int loongarch_have_legacy_bpi (void){ return have_bpi; } From a7a174d80e0dad65d1eea1d0380d1b6d7fc73be3 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Tue, 13 Aug 2024 16:05:28 +0800 Subject: [PATCH 454/521] AOSCOS: loongarch: fix DMA address offset Loongarch CPUs are using HT bus interface, which is only 40-bit wide, to communicate with the bridge chipset. However, the actual memory bus of the CPUs is 48-bit wide, and the available address space of DMA requests of PCI devices is also larger than the 40-bit address space. The address space of loongarch systems is not continuous, the bits [47:44] of which are used to denote the belonging node id, which is far beyond the space provided by the 40-bit wide HT bus. As a result, a translation on the both LS7A and CPU sides is needed. The translation happens on the LS7A side is controlled by the higher half of the register, HT_ROUTE. Bits [12:8] denotes dma_node_id_offset and bits[15:13] denotes dma_node_id_offset_mapped. The behavior of the translation is that the chip extracts the node id from bit dma_node_id_offset + 36 of a DMA address, places it at bit dma_node_id_offset_mapped + 32, and generates the address on the HT bus. On the CPU side, an alike translation happens, to convert the address on the HT bus back to a proper memory address. On machines with legacy firmware with BPI01000 version, dma_node_id_offset is configured with 0, resulting the address which should be used by the DMA engine of a PCI device differs from the actural physical memeory address, which requires a pair of arch-specific phys_to_dma and dma_to_phys functions or setting up the whole mapping in the _DMA method of the PCI root device in the DSDT table. The former method requires we add back the prevoiusly removed functions, and the latter method degrades the performance since when the translation happens, the mapping table is scanned linearly. This patch addresses this issue by directly setting dma_node_id_offset to 8, like what is done by modern firmwares, making the address used by the DMA engine just the same as the actual physical memory address, and eliminating the need of DMA address translation on the kernel side. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/legacy_boot.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 3ec69ffd8b9420..2888e2b9fb4566 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -46,6 +46,19 @@ enum bpi_mem_type { #define MSI_MSG_ADDRESS 0x2FF00000 #define MSI_MSG_DEFAULT_COUNT 0xC0 +/* + ``This should be configured by firmware" + -- Section 4, Page 25 of Loongson-7A1000 User Manual v2.0 + but unable to figure out the exact value. + The manual gives a typical value in Table 3.2, Page 23. +*/ +#define LS7A_CHIPCFG_REG_OFF 0x00010000 + +/* Section 4.1, Page 26 of Loongson-7A1000 User Manual v2.0 */ +#define LS7A_DMA_CFG_OFF 0x041c +#define LS7A_DMA_NODE_ID_OFFSET_SHF 8 +#define LS7A_DMA_NODE_ID_OFFSET_MASK GENMASK(12, 8) + struct loongarch_bpi_mem_map { struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ u8 map_count; @@ -588,6 +601,20 @@ static void __init init_acpi_arch_os_table_override (struct acpi_table_header *e new_madt->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_madt, new_madt_size); new_mcfg->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_mcfg, new_mcfg_size); *new_table = (struct acpi_table_header *)new_madt; + + // Override LS7A dma_node_id_offset + for(int i = 0; i < io_apic_count; i++){ + u64 ls7a_base_addr = bio_pics[i].address; + void __iomem *dma_node_id_addr = (void __iomem *) TO_UNCACHE(ls7a_base_addr + LS7A_CHIPCFG_REG_OFF + LS7A_DMA_CFG_OFF); + u32 dma_cfg = readl(dma_node_id_addr); + u32 dma_node_id_offset = (dma_cfg & LS7A_DMA_NODE_ID_OFFSET_MASK) >> LS7A_DMA_NODE_ID_OFFSET_SHF; + if(dma_node_id_offset != 8){ + pr_info("BPI: LS7A %d DMA node id offset is %d, will set to 8\n", i, dma_node_id_offset); + dma_cfg &= ~LS7A_DMA_NODE_ID_OFFSET_MASK; + dma_cfg |= 8 << LS7A_DMA_NODE_ID_OFFSET_SHF; + writel(dma_cfg, dma_node_id_addr); + } + } } void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ From 777173418bbb3317dfe46bfa1421f92b50947431 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Tue, 13 Aug 2024 22:33:01 +0800 Subject: [PATCH 455/521] AOSCOS: loongarch: fix HT_RX_INT_TRANS register On machines with legacy firmware with BPI01000 version, the HT_RX_INT_TRANS register on the node 5, i.e. the node connected with the second LS7A bridge chip, is not initialized correctly, causing the failure of the delivery of interrupts from PCIe devices connected to the second LS7A bridge chip. This patch fixes this by correctly pointing the HT_RX_INT_TRANS register to the EXT_IOI_SEND_OFF register on the corresponding node. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/legacy_boot.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 2888e2b9fb4566..204b33109f37db 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -59,6 +59,16 @@ enum bpi_mem_type { #define LS7A_DMA_NODE_ID_OFFSET_SHF 8 #define LS7A_DMA_NODE_ID_OFFSET_MASK GENMASK(12, 8) +/* Section 14.4.1, Page 100 of Loongson-3A5000 User Manual v1.03 */ +#define HT_CTRL_CFG_OFF 0xfdfb000000ull +/* Section 14.5.34, Page 141 of Loongson-3A5000 User Manual v1.03 */ +#define HT_CTRL_HT_RX_INT_TRANS_LO_OFF 0x270 +#define HT_CTRL_HT_RX_INT_TRANS_HI_OFF 0x274 +#define HT_CTRL_HT_RX_INT_TRANS_INT_TRANS_ALLOW BIT(30) + +/* Section 11.2.3, Page 61 of Loongson-3A5000 User Manual v1.03 */ +#define EXT_IOI_SEND_OFF 0x1140 + struct loongarch_bpi_mem_map { struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ u8 map_count; @@ -615,6 +625,20 @@ static void __init init_acpi_arch_os_table_override (struct acpi_table_header *e writel(dma_cfg, dma_node_id_addr); } } + + // Override HT_RX_INT_TRANS + for(int i = 0; i < io_apic_count; i++){ + unsigned int node = eio_pics[i].node; + void __iomem *ht_rx_int_trans_hi = (void __iomem *) TO_UNCACHE(nid_to_addrbase(node) + HT1LO_OFFSET + HT_CTRL_CFG_OFF + HT_CTRL_HT_RX_INT_TRANS_HI_OFF); + void __iomem *ht_rx_int_trans_lo = (void __iomem *) TO_UNCACHE(nid_to_addrbase(node) + HT1LO_OFFSET + HT_CTRL_CFG_OFF + HT_CTRL_HT_RX_INT_TRANS_LO_OFF); + u64 ext_ioi_addr = nid_to_addrbase(node) + LOONGSON_REG_BASE + EXT_IOI_SEND_OFF; + u64 ht_rx_int_trans_cfg_orig = ((u64)readl(ht_rx_int_trans_hi) << 32) | readl(ht_rx_int_trans_lo); + u32 ht_rx_int_trans_cfg_hi = HT_CTRL_HT_RX_INT_TRANS_INT_TRANS_ALLOW | (ext_ioi_addr >> 32); + u32 ht_rx_int_trans_cfg_lo = ext_ioi_addr & GENMASK(31, 0); + pr_info("BPI: HT controller on node %u RX_INT_TRANS is 0x%llx, will set to 0x%llx\n", node, ht_rx_int_trans_cfg_orig, ((u64)ht_rx_int_trans_cfg_hi << 32) | ht_rx_int_trans_cfg_lo); + writel(ht_rx_int_trans_cfg_hi, ht_rx_int_trans_hi); + writel(ht_rx_int_trans_cfg_lo, ht_rx_int_trans_lo); + } } void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ From 6fc3740395fa16871ff8bd87497d38548a6964b2 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Wed, 28 Aug 2024 03:09:24 +0800 Subject: [PATCH 456/521] AOSCOS: arch/loongarch: add la_ow_syscall as in-tree module Signed-off-by: Miao Wang Link: https://github.com/AOSC-Dev/la_ow_syscall/tree/492c16dc5f6d2a8606b25077086a42cb4b0347ea Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/Kbuild | 2 + arch/loongarch/ow_syscall/Kbuild | 38 ++ arch/loongarch/ow_syscall/LICENSE | 339 ++++++++++++++++++ arch/loongarch/ow_syscall/Makefile | 37 ++ arch/loongarch/ow_syscall/README.md | 77 ++++ arch/loongarch/ow_syscall/VERSION | 1 + arch/loongarch/ow_syscall/dkms.conf.in | 11 + arch/loongarch/ow_syscall/feat_test.c | 16 + arch/loongarch/ow_syscall/fsstat.c | 87 +++++ arch/loongarch/ow_syscall/fsstat.h | 4 + .../loongarch/ow_syscall/la_ow_syscall_main.c | 274 ++++++++++++++ arch/loongarch/ow_syscall/signal.c | 239 ++++++++++++ arch/loongarch/ow_syscall/signal.h | 44 +++ arch/loongarch/ow_syscall/systable.c | 65 ++++ arch/loongarch/ow_syscall/systable.h | 8 + 15 files changed, 1242 insertions(+) create mode 100644 arch/loongarch/ow_syscall/Kbuild create mode 100644 arch/loongarch/ow_syscall/LICENSE create mode 100644 arch/loongarch/ow_syscall/Makefile create mode 100644 arch/loongarch/ow_syscall/README.md create mode 100644 arch/loongarch/ow_syscall/VERSION create mode 100644 arch/loongarch/ow_syscall/dkms.conf.in create mode 100644 arch/loongarch/ow_syscall/feat_test.c create mode 100644 arch/loongarch/ow_syscall/fsstat.c create mode 100644 arch/loongarch/ow_syscall/fsstat.h create mode 100644 arch/loongarch/ow_syscall/la_ow_syscall_main.c create mode 100644 arch/loongarch/ow_syscall/signal.c create mode 100644 arch/loongarch/ow_syscall/signal.h create mode 100644 arch/loongarch/ow_syscall/systable.c create mode 100644 arch/loongarch/ow_syscall/systable.h diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild index 1c7a0dbe5e72f2..c0857841e3d197 100644 --- a/arch/loongarch/Kbuild +++ b/arch/loongarch/Kbuild @@ -7,3 +7,5 @@ obj-$(subst m,y,$(CONFIG_KVM)) += kvm/ # for cleaning subdir- += boot + +obj-y += ow_syscall/ diff --git a/arch/loongarch/ow_syscall/Kbuild b/arch/loongarch/ow_syscall/Kbuild new file mode 100644 index 00000000000000..65cd78995176f9 --- /dev/null +++ b/arch/loongarch/ow_syscall/Kbuild @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for LoongArch old-world syscall compatible layer. +# + +ifdef KBUILD_EXTMOD +CONFIG_LOONGARCH_OW_SYSCALL := m +endif + +obj-$(CONFIG_LOONGARCH_OW_SYSCALL) += la_ow_syscall.o +la_ow_syscall-y += fsstat.o la_ow_syscall_main.o signal.o systable.o +targets += feat_test.o + +$(obj)/kernel_feature.h: $(obj)/feat_test.syms + @$(kecho) ' GEN $@' + $(Q)if grep "kernel_have_new_stat" $< >/dev/null; then \ + echo "#define KERNEL_HAVE_NEW_STAT"; \ + else \ + echo "/* KERNEL_HAVE_NEW_STAT is not defined */"; \ + fi > $@ + $(Q)if grep "kernel_have_systbl" $< >/dev/null; then \ + echo "#define KERNEL_HAVE_SYSTBL"; \ + else \ + echo "/* KERNEL_HAVE_SYSTBL is not defined */"; \ + fi >> $@ + +$(obj)/feat_test.syms: $(obj)/feat_test.o + @$(kecho) ' GEN $@' + $(Q)$(OBJDUMP) -t --section=.text $< >$@ + +$(obj)/module_version.h: $(src)/VERSION + @$(kecho) ' GEN $@' + $(Q)echo "#define THIS_MODULE_VERSION \"$(shell cat $<)\"" > $@ + +$(obj)/systable.o $(obj)/fsstat.o: $(obj)/kernel_feature.h +$(obj)/la_ow_syscall_main.o: $(obj)/module_version.h + +clean-files += kernel_feature.h feat_test.syms module_version.h diff --git a/arch/loongarch/ow_syscall/LICENSE b/arch/loongarch/ow_syscall/LICENSE new file mode 100644 index 00000000000000..d159169d105089 --- /dev/null +++ b/arch/loongarch/ow_syscall/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/arch/loongarch/ow_syscall/Makefile b/arch/loongarch/ow_syscall/Makefile new file mode 100644 index 00000000000000..e975aa1cdb7af8 --- /dev/null +++ b/arch/loongarch/ow_syscall/Makefile @@ -0,0 +1,37 @@ +obj-m += la_ow_syscall.o fsstat.o la_ow_syscall_main.o signal.o + +KVER ?= $(shell uname -r) +KDIR ?= /lib/modules/$(KVER)/build +VERSION ?= $(shell cat VERSION) + +default: + $(MAKE) -C $(KDIR) M=$(CURDIR) modules + +clean: + $(MAKE) -C $(KDIR) M=$(CURDIR) clean + +install: + $(MAKE) -C $(KDIR) M=$(CURDIR) modules_install + +dkms.conf: dkms.conf.in + m4 -DVERSION=$(VERSION) $^ > $@ + +dkms-add: dkms.conf + /usr/sbin/dkms add $(CURDIR) + +dkms-build: dkms.conf + /usr/sbin/dkms build la_ow_syscall/$(VERSION) + +dkms-install: dkms.conf + /usr/sbin/dkms install la_ow_syscall/$(VERSION) + +dkms-remove: dkms.conf + /usr/sbin/dkms remove la_ow_syscall/$(VERSION) --all + +modprobe-install: + modprobe la_ow_syscall + +modprobe-remove: + modprobe -r la_ow_syscall + +dev: modprobe-remove dkms-remove dkms-add dkms-build dkms-install modprobe-install diff --git a/arch/loongarch/ow_syscall/README.md b/arch/loongarch/ow_syscall/README.md new file mode 100644 index 00000000000000..25c6757983896f --- /dev/null +++ b/arch/loongarch/ow_syscall/README.md @@ -0,0 +1,77 @@ +la\_ow\_syscall +==== + +This kernel modules provides compatibility with LoongArch's old-world ABI, +making it possible to run old-world applications (such as Kingsoft's WPS Office +and Tencent QQ) transparently on new-world (ABI 2.0) kernels and userspaces. + +Requirements +---- + +Linux Kernel >= 6.1.0 for `loongarch64` with the following option(s) set: + +- `CONFIG_KALLSYMS=y` (for reading kernel symbol addresses). +- `CONFIG_KPROBES=y` (for probing kernel symbol addresses using kernels where + base address randomisation - `CONFIG_RANDOMIZE_BASE` - is enabled). + +Installation +---- + +You may install this kernel both as an in-tree module, an out-of-tree DKMS +dynamic module, or a version-specific module. You may pick any option that best +suits your needs. + +### In-tree module + +Copy this source tree as `arch/loongarch/ow_syscall` in your kernel tree and +append the following to `arch/loongarch/Kbuild`: + +``` +obj-y += ow_syscall/ +``` + +After building the kernel with `make`, run the following command to build the +kernel module: + +``` +# $PWD is containing built objects +# /path/to/source_dir is containing Linux source code +make \ + -C /path/to/source_dir \ + ARCH=loongarch \ + O="$PWD" \ + arch/loongarch/ow_syscall/la_ow_syscall.ko \ + CONFIG_LOONGARCH_OW_SYSCALL=m +``` + +Upon completion, copy the kernel module in place +(`/lib/modules/.../arch/loongarch/ow_syscall/la_ow_syscall.ko`) and +re-generate modules.dep and map files: + +``` +depmod +``` + +### DKMS dynamic module + +Generate a `dkms.conf`: + +``` +make dkms.conf +``` + +For installation and version management, refer to dkms(8) for details. + +### Version-specific module + +Build the kernel module: + +``` +make +``` + +Load the module with super user or root privilege: + +``` +insmod la_ow_syscall.ko +``` diff --git a/arch/loongarch/ow_syscall/VERSION b/arch/loongarch/ow_syscall/VERSION new file mode 100644 index 00000000000000..17e51c385ea382 --- /dev/null +++ b/arch/loongarch/ow_syscall/VERSION @@ -0,0 +1 @@ +0.1.1 diff --git a/arch/loongarch/ow_syscall/dkms.conf.in b/arch/loongarch/ow_syscall/dkms.conf.in new file mode 100644 index 00000000000000..ad9aec128e6f8e --- /dev/null +++ b/arch/loongarch/ow_syscall/dkms.conf.in @@ -0,0 +1,11 @@ +PACKAGE_NAME="la_ow_syscall" +PACKAGE_VERSION="VERSION" + +BUILD_EXCLUSIVE_KERNEL_MIN="5.19" +BUILD_EXCLUSIVE_CONFIG="CONFIG_KALLSYMS" + +MAKE="KDIR=/lib/modules/${kernelver}/build make" +CLEAN="make clean" +BUILT_MODULE_NAME[0]="$PACKAGE_NAME" +AUTOINSTALL="yes" +DEST_MODULE_LOCATION[0]="/extra" diff --git a/arch/loongarch/ow_syscall/feat_test.c b/arch/loongarch/ow_syscall/feat_test.c new file mode 100644 index 00000000000000..5fc3b8709364d4 --- /dev/null +++ b/arch/loongarch/ow_syscall/feat_test.c @@ -0,0 +1,16 @@ +/* + This file is used to test if the coresponding macro is defined in unistd.h + If a macro is defined, the coresponding function will be defined, and + the symbol will be shown in the .o file. Later, the Kbuild script will + generate kernel_feature.h according to the existence of the symbols. +*/ + +#include +#ifdef __ARCH_WANT_NEW_STAT +void kernel_have_new_stat(void); +void kernel_have_new_stat(void){ } +#endif +#ifndef __SYSCALL +void kernel_have_systbl(void); +void kernel_have_systbl(void){ } +#endif diff --git a/arch/loongarch/ow_syscall/fsstat.c b/arch/loongarch/ow_syscall/fsstat.c new file mode 100644 index 00000000000000..1f5c336f52ec15 --- /dev/null +++ b/arch/loongarch/ow_syscall/fsstat.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include "fsstat.h" +#include "kernel_feature.h" + +#ifndef KERNEL_HAVE_NEW_STAT +#define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st)) + +struct __old_kernel_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned int st_size; + unsigned int st_atime; + unsigned int st_mtime; + unsigned int st_ctime; +}; + +static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) +{ + struct stat tmp; + + if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev)) + return -EOVERFLOW; + if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev)) + return -EOVERFLOW; +#if BITS_PER_LONG == 32 + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; +#endif + + INIT_STRUCT_STAT_PADDING(tmp); + tmp.st_dev = new_encode_dev(stat->dev); + tmp.st_ino = stat->ino; + if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) + return -EOVERFLOW; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + if (tmp.st_nlink != stat->nlink) + return -EOVERFLOW; + SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + tmp.st_rdev = new_encode_dev(stat->rdev); + tmp.st_size = stat->size; + tmp.st_atime = stat->atime.tv_sec; + tmp.st_mtime = stat->mtime.tv_sec; + tmp.st_ctime = stat->ctime.tv_sec; +#ifdef STAT_HAVE_NSEC + tmp.st_atime_nsec = stat->atime.tv_nsec; + tmp.st_mtime_nsec = stat->mtime.tv_nsec; + tmp.st_ctime_nsec = stat->ctime.tv_nsec; +#endif + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; + return copy_to_user(statbuf, &tmp, sizeof(tmp)) ? -EFAULT : 0; +} + +__SYSCALL_DEFINEx(2, _newfstat, unsigned int, fd, struct stat __user *, statbuf) +{ + struct kstat stat; + int error = p_vfs_fstat(fd, &stat); + + if (!error) + error = cp_new_stat(&stat, statbuf); + + return error; +} + +__SYSCALL_DEFINEx(4, _newfstatat, int, dfd, const char __user *, filename, + struct stat __user *, statbuf, int, flag) +{ + struct kstat stat; + int error; + + error = p_vfs_fstatat(dfd, filename, &stat, flag); + if (error) + return error; + return cp_new_stat(&stat, statbuf); +} +#endif diff --git a/arch/loongarch/ow_syscall/fsstat.h b/arch/loongarch/ow_syscall/fsstat.h new file mode 100644 index 00000000000000..fa40914afa4141 --- /dev/null +++ b/arch/loongarch/ow_syscall/fsstat.h @@ -0,0 +1,4 @@ +#include +extern int (*p_vfs_fstatat)(int dfd, const char __user *filename, + struct kstat *stat, int flags); +extern int (*p_vfs_fstat)(int fd, struct kstat *stat); diff --git a/arch/loongarch/ow_syscall/la_ow_syscall_main.c b/arch/loongarch/ow_syscall/la_ow_syscall_main.c new file mode 100644 index 00000000000000..4ae3fc6d707851 --- /dev/null +++ b/arch/loongarch/ow_syscall/la_ow_syscall_main.c @@ -0,0 +1,274 @@ +#include /* Needed by all modules */ +#include /* Needed for KERN_INFO */ +#include /* Needed for the macros */ +#include /* Needed for kprobe calls */ +#include "module_version.h" + +///< The license type -- this affects runtime behavior +MODULE_LICENSE("GPL"); + +///< The author -- visible when you use modinfo +MODULE_AUTHOR("Miao Wang"); + +///< The description -- see modinfo +MODULE_DESCRIPTION("LoongArch old-world syscall compatibility module"); + +///< The version of the module +MODULE_VERSION(THIS_MODULE_VERSION); + +#include +#include + +#include "systable.h" + +#define __EXTERN +#include "fsstat.h" +#include "signal.h" + +#ifndef __loongarch64 +#error This Linux kernel module is only supported on LoongArch +#endif + +static unsigned long kallsyms_lookup_name_addr = 0; +static unsigned int allow_mod_unreg = 0; + +#include + +// Taken from https://github.com/zizzu0/LinuxKernelModules/blob/main/FindKallsymsLookupName.c +#define KPROBE_PRE_HANDLER(fname) \ + static int __kprobes fname(struct kprobe *p, struct pt_regs *regs) + +static struct kprobe kp0, kp1; + +KPROBE_PRE_HANDLER(handler_pre0) +{ + kallsyms_lookup_name_addr = regs->csr_era; + + return 0; +} + +KPROBE_PRE_HANDLER(handler_pre1) +{ + return 0; +} + +#undef KPROBE_PRE_HANDLER + +static int do_register_kprobe(struct kprobe *kp, char *symbol_name, + void *handler) +{ + int ret = 0; + + kp->symbol_name = symbol_name; + kp->pre_handler = handler; + + ret = register_kprobe(kp); + if (ret < 0) { + pr_err("register_probe() for symbol %s failed, returned %d\n", + symbol_name, ret); + return ret; + } + + pr_debug("planted kprobe for symbol %s at %p\n", symbol_name, kp->addr); + + return ret; +} + +static int __init kprobe_kallsyms_lookup_name(void) +{ + int ret = 0; + + ret = do_register_kprobe(&kp0, "kallsyms_lookup_name", handler_pre0); + if (ret < 0) + return ret; + + ret = do_register_kprobe(&kp1, "kallsyms_lookup_name", handler_pre1); + if (ret < 0) { + unregister_kprobe(&kp0); + return ret; + } + + unregister_kprobe(&kp0); + unregister_kprobe(&kp1); + + return ret; +} + + +static int __init find_kallsyms_lookup_name(void) +{ + char fn_name[KSYM_SYMBOL_LEN]; + + int ret = 0; + + if (kallsyms_lookup_name_addr == 0 || + kallsyms_lookup_name_addr == (unsigned long)-1) { + ret = kprobe_kallsyms_lookup_name(); + if ( ret < 0 ) { + return ret; + } + if (kallsyms_lookup_name_addr == 0 || + kallsyms_lookup_name_addr == (unsigned long)-1) { + return -EINVAL; + } + } + sprint_symbol(fn_name, kallsyms_lookup_name_addr); + if (strncmp(fn_name, "kallsyms_lookup_name+0x0", + strlen("kallsyms_lookup_name+0x0")) == 0) { + pr_debug("got kallsyms_lookup_name = %lx\n", + kallsyms_lookup_name_addr); + return 0; + } else { + pr_debug("got %s at %lx, not kallsyms_lookup_name\n", fn_name, + kallsyms_lookup_name_addr); + return -EINVAL; + } +} + +int (*p_vfs_fstatat)(int dfd, const char __user *filename, struct kstat *stat, + int flags); +int (*p_vfs_fstat)(int fd, struct kstat *stat); + +void *p_sys_setxattr, *p_sys_close, *p_sys_clone; + +static struct { + const char *func_name; + void **stor; +} relocation_table[] = { +#define __rel(func) \ + { \ + (#func), ((void **)&(p_##func)) \ + } + __rel(vfs_fstatat), __rel(vfs_fstat), + __rel(sys_setxattr), __rel(sys_close), + __rel(sys_clone), __rel(sys_rt_sigprocmask), + __rel(sys_rt_sigpending), __rel(sys_rt_sigtimedwait), + __rel(sys_rt_sigaction), __rel(sys_rt_sigsuspend), + __rel(sys_pselect6), __rel(sys_ppoll), +#ifdef CONFIG_SIGNALFD + __rel(sys_signalfd4), +#endif +#ifdef CONFIG_EPOLL + __rel(sys_epoll_pwait), __rel(sys_epoll_pwait2), +#endif +}; +#define nr_rel_tab (sizeof(relocation_table) / sizeof(relocation_table[0])) + +static void **p_sys_call_table; + +#include +#include +static int __init find_sys_call_table(void) +{ + unsigned long (*p_kallsyms_lookup_name)(const char *name) = + (void *)kallsyms_lookup_name_addr; + unsigned long *sys_table; + + if (kallsyms_lookup_name_addr == 0) { + return -ENOSYS; + } + + if ((sys_table = (unsigned long *)p_kallsyms_lookup_name( + "sys_call_table"))) { + p_sys_call_table = (void **)sys_table; + pr_debug("found sys_call_table=%px\n", p_sys_call_table); + return 0; + } + + pr_info("failed to find sys_call_table using kallsyms_lookup_name()\n"); + pr_info("trying to find sys_call_table using memory scanning\n"); + + for (sys_table = (void *)&jiffies; + (void *)sys_table < (void *)&reboot_mode; sys_table++) { + if (sys_table[__NR_setxattr] == (unsigned long)p_sys_setxattr && + sys_table[__NR_close] == (unsigned long)p_sys_close && + sys_table[__NR_clone] == (unsigned long)p_sys_clone) { + p_sys_call_table = (void **)sys_table; + pr_debug("found sys_call_table=%px\n", + p_sys_call_table); + return 0; + } + } + + return -ENOSYS; +} + +static int __init oldsyscall_start(void) +{ + unsigned long (*p_kallsyms_lookup_name)(const char *name); + int rc = find_kallsyms_lookup_name(); + if (rc < 0) { + return rc; + } + p_kallsyms_lookup_name = (void *)kallsyms_lookup_name_addr; + + for (int i = 0; i < nr_rel_tab; i++) { + unsigned long p = + p_kallsyms_lookup_name(relocation_table[i].func_name); + if (p == 0) { + pr_warn("cannot find symbol %s\n", + relocation_table[i].func_name); + return -EINVAL; + } + pr_debug("found symbol %s at %px\n", + relocation_table[i].func_name, (void *)p); + *relocation_table[i].stor = (void *)p; + } + rc = find_sys_call_table(); + if (rc < 0) { + return rc; + } + for (int i = 0; syscall_to_replace[i].syscall_num != -1; i++) { + if (syscall_to_replace[i].symbol_addr) { + continue; + } + const char *symbol_name = + sys_call_table_name[syscall_to_replace[i].syscall_num]; + unsigned long symbol_addr = p_kallsyms_lookup_name(symbol_name); + if (symbol_addr) { + pr_debug("found %s at %px\n", symbol_name, + (void *)symbol_addr); + } else { + pr_warn("cannot find symbol %s\n", symbol_name); + return -EINVAL; + } + syscall_to_replace[i].symbol_addr = (void *)symbol_addr; + } + if (!allow_mod_unreg) { + bool succ = try_module_get(THIS_MODULE); + if (!succ) { + return -EINVAL; + } + } + for (int i = 0; syscall_to_replace[i].syscall_num != -1; i++) { + pr_debug("will replace syscall_%ld with %px, orig %px\n", + syscall_to_replace[i].syscall_num, + syscall_to_replace[i].symbol_addr, + p_sys_call_table[syscall_to_replace[i].syscall_num]); + syscall_to_replace[i].orig = + p_sys_call_table[syscall_to_replace[i].syscall_num]; + p_sys_call_table[syscall_to_replace[i].syscall_num] = + syscall_to_replace[i].symbol_addr; + } + pr_info("la_ow_syscall module successfully initialized\n"); + return 0; +} + +static void __exit oldsyscall_end(void) +{ + for (int i = 0; syscall_to_replace[i].syscall_num != -1; i++) { + pr_debug("will restore syscall_%ld to %px\n", + syscall_to_replace[i].syscall_num, + syscall_to_replace[i].orig); + p_sys_call_table[syscall_to_replace[i].syscall_num] = + syscall_to_replace[i].orig; + } +} + +module_init(oldsyscall_start); +module_exit(oldsyscall_end); +module_param(allow_mod_unreg, uint, 0000); +MODULE_PARM_DESC(allow_mod_unreg, + "Allow this module to be unload (Danger! Debug use only)"); +module_param(kallsyms_lookup_name_addr, ulong, 0000); +MODULE_PARM_DESC(kallsyms_lookup_name_addr, "Address for kallsyms_lookup_name, provide this when unable to find using kprobe"); diff --git a/arch/loongarch/ow_syscall/signal.c b/arch/loongarch/ow_syscall/signal.c new file mode 100644 index 00000000000000..90eb34727680dc --- /dev/null +++ b/arch/loongarch/ow_syscall/signal.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include "signal.h" + +#define _LA_OW_NSIG 128 +#define _LA_OW_NSIG_WORDS (_LA_OW_NSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_LA_OW_NSIG_WORDS]; +} _la_ow_sigset_t; + +static inline int clear_user_sigset_extension(sigset_t __user *to) +{ + char __user *expansion = (char __user *)to + sizeof(sigset_t); + int rc = clear_user(expansion, + sizeof(_la_ow_sigset_t) - sizeof(sigset_t)); + if (rc < 0) { + return -EFAULT; + } + return 0; +} + +__SYSCALL_DEFINEx(4, _rt_sigprocmask, int, how, sigset_t __user *, nset, + sigset_t __user *, oset, size_t, sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigprocmask(how, nset, oset, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = + p_sys_rt_sigprocmask(how, nset, oset, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + if (oset) { + int rc2 = clear_user_sigset_extension(oset); + if (rc2 < 0) { + return rc2; + } + } + return 0; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(2, _rt_sigpending, sigset_t __user *, uset, size_t, + sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigpending(uset, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigpending(uset, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + int rc2 = clear_user_sigset_extension(uset); + if (rc2 < 0) { + return rc2; + } + return 0; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(4, _rt_sigtimedwait, const sigset_t __user *, uthese, + siginfo_t __user *, uinfo, + const struct __kernel_timespec __user *, uts, size_t, + sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigtimedwait(uthese, uinfo, uts, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigtimedwait(uthese, uinfo, uts, + sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(4, _rt_sigaction, int, sig, const struct sigaction __user *, + act, struct sigaction __user *, oact, size_t, sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigaction(sig, act, oact, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigaction(sig, act, oact, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + if (oact) { + int rc2 = clear_user_sigset_extension(&oact->sa_mask); + if (rc2 < 0) { + return rc2; + } + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(2, _rt_sigsuspend, sigset_t __user *, unewset, size_t, + sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigsuspend(unewset, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigsuspend(unewset, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(6, _pselect6, int, n, fd_set __user *, inp, fd_set __user *, + outp, fd_set __user *, exp, struct __kernel_timespec __user *, + tsp, void __user *, sig) +{ + struct sigset_argpack { + sigset_t __user *p; + size_t size; + } x = { NULL, 0 }; + struct sigset_argpack __user *siginfo = + (struct sigset_argpack __user *)sig; + + if (siginfo) { + int rc = get_user(x.size, &siginfo->size); + if (rc < 0) { + return -EFAULT; + } + } + if (siginfo == NULL || x.size == sizeof(sigset_t)) { + return p_sys_pselect6(n, inp, outp, exp, tsp, sig); + } else if (x.size == sizeof(_la_ow_sigset_t)) { + int rc = put_user(sizeof(sigset_t), &siginfo->size); + if (rc < 0) { + return -EFAULT; + } + rc = p_sys_pselect6(n, inp, outp, exp, tsp, sig); + int rc2 = put_user(sizeof(_la_ow_sigset_t), &siginfo->size); + if (rc2 < 0) { + return -EFAULT; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(5, _ppoll, struct pollfd __user *, ufds, unsigned int, nfds, + struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask, + size_t, sigsetsize) +{ + if (sigmask == NULL || sigsetsize == sizeof(sigset_t)) { + return p_sys_ppoll(ufds, nfds, tsp, sigmask, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_ppoll(ufds, nfds, tsp, sigmask, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +#ifdef CONFIG_EPOLL + +__SYSCALL_DEFINEx(6, _epoll_pwait, int, epfd, struct epoll_event __user *, + events, int, maxevents, int, timeout, const sigset_t __user *, + sigmask, size_t, sigsetsize) +{ + if (sigmask == NULL || sigsetsize == sizeof(sigset_t)) { + return p_sys_epoll_pwait(epfd, events, maxevents, timeout, + sigmask, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_epoll_pwait(epfd, events, maxevents, timeout, + sigmask, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(6, _epoll_pwait2, int, epfd, struct epoll_event __user *, + events, int, maxevents, + const struct __kernel_timespec __user *, timeout, + const sigset_t __user *, sigmask, size_t, sigsetsize) +{ + if (sigmask == NULL || sigsetsize == sizeof(sigset_t)) { + return p_sys_epoll_pwait2(epfd, events, maxevents, timeout, + sigmask, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_epoll_pwait2(epfd, events, maxevents, timeout, + sigmask, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +#endif + +#ifdef CONFIG_SIGNALFD + +__SYSCALL_DEFINEx(4, _signalfd4, int, ufd, sigset_t __user *, user_mask, size_t, + sizemask, int, flags) +{ + if (sizemask == sizeof(sigset_t)) { + return p_sys_signalfd4(ufd, user_mask, sizemask, flags); + } else if (sizemask == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_signalfd4(ufd, user_mask, sizeof(sigset_t), + flags); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +#endif diff --git a/arch/loongarch/ow_syscall/signal.h b/arch/loongarch/ow_syscall/signal.h new file mode 100644 index 00000000000000..adcd53bedc662d --- /dev/null +++ b/arch/loongarch/ow_syscall/signal.h @@ -0,0 +1,44 @@ +#include + +#ifndef __EXTERN +#define __EXTERN extern +#endif +#define P__SYSCALL_DEFINEx(x, name, ...) \ + __EXTERN int (*p_sys##name)(__MAP(x, __SC_DECL, __VA_ARGS__)) + +P__SYSCALL_DEFINEx(4, _rt_sigprocmask, int, how, sigset_t __user *, nset, + sigset_t __user *, oset, size_t, sigsetsize); + +P__SYSCALL_DEFINEx(2, _rt_sigpending, sigset_t __user *, uset, size_t, + sigsetsize); + +P__SYSCALL_DEFINEx(4, _rt_sigtimedwait, const sigset_t __user *, uthese, + siginfo_t __user *, uinfo, + const struct __kernel_timespec __user *, uts, size_t, + sigsetsize); + +P__SYSCALL_DEFINEx(4, _rt_sigaction, int, sig, const struct sigaction __user *, + act, struct sigaction __user *, oact, size_t, sigsetsize); + +P__SYSCALL_DEFINEx(2, _rt_sigsuspend, sigset_t __user *, unewset, size_t, + sigsetsize); +P__SYSCALL_DEFINEx(6, _pselect6, int, n, fd_set __user *, inp, fd_set __user *, + outp, fd_set __user *, exp, + struct __kernel_timespec __user *, tsp, void __user *, sig); +P__SYSCALL_DEFINEx(5, _ppoll, struct pollfd __user *, ufds, unsigned int, nfds, + struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask, + size_t, sigsetsize); +#ifdef CONFIG_SIGNALFD +P__SYSCALL_DEFINEx(4, _signalfd4, int, ufd, sigset_t __user *, user_mask, + size_t, sizemask, int, flags); +#endif +#ifdef CONFIG_EPOLL +P__SYSCALL_DEFINEx(6, _epoll_pwait, int, epfd, struct epoll_event __user *, + events, int, maxevents, int, timeout, + const sigset_t __user *, sigmask, size_t, sigsetsize); +P__SYSCALL_DEFINEx(6, _epoll_pwait2, int, epfd, struct epoll_event __user *, + events, int, maxevents, + const struct __kernel_timespec __user *, timeout, + const sigset_t __user *, sigmask, size_t, sigsetsize); +#undef P__SYSCALL_DEFINEx +#endif diff --git a/arch/loongarch/ow_syscall/systable.c b/arch/loongarch/ow_syscall/systable.c new file mode 100644 index 00000000000000..8b311954d62ef1 --- /dev/null +++ b/arch/loongarch/ow_syscall/systable.c @@ -0,0 +1,65 @@ +#include "systable.h" +#include "kernel_feature.h" + +/* + In 6.11 or newer, the asm-generic/unistd.h is not used by the loongarch + asm/unistd.h. Instead, the definition of the syscall table and the syscall + numbers is generated after commit 26a3b85bac08 ("loongarch: convert to + generic syscall table"). As a result, we can no more get the definition of + those syscalls that are not available on Loongarch. + + To solve this problem, we still use asm-generic/unistd.h to retrive the + definitions. + + To avoid any possible conflicts, only syscall related data are defined here. + No functions are implemented here, so the above hacks will not affect other + parts of our module and will not introduce unexpected changes related to the + syscall numbers, ensuring the consistence of our module with the kernel. +*/ + +#include + +/* + And we also need those hacks to prevent including asm/unistd.h, which will + cause redefinition of the syscall numbers. +*/ + +#define _LINUX_UNISTD_H_ /* prevent loading uapi/linux/unistd.h */ +#define __ASM_VDSO_H /* prevent loading asm/vdso.h */ +#define _ASM_SECCOMP_H /* prevent loading asm/seccomp.h */ +#include + +#define __ARCH_WANT_SET_GET_RLIMIT +#define __ARCH_WANT_NEW_STAT + +#undef __SYSCALL +#define __SYSCALL(nr, call) [nr] = (#call), + +const char *sys_call_table_name[__NR_syscalls] = { + [0 ... __NR_syscalls - 1] = "sys_ni_syscall", +#include +}; + +struct syscall_replace_table syscall_to_replace[] = { +#ifndef KERNEL_HAVE_NEW_STAT + { __NR_fstat, sys_newfstat }, + { __NR_newfstatat, sys_newfstatat }, +#endif + { __NR_getrlimit, NULL }, + { __NR_setrlimit, NULL }, + { __NR_rt_sigprocmask, sys_rt_sigprocmask }, + { __NR_rt_sigpending, sys_rt_sigpending }, + { __NR_rt_sigtimedwait, sys_rt_sigtimedwait }, + { __NR_rt_sigaction, sys_rt_sigaction }, + { __NR_rt_sigsuspend, sys_rt_sigsuspend }, + { __NR_pselect6, sys_pselect6 }, + { __NR_ppoll, sys_ppoll }, +#ifdef CONFIG_SIGNALFD + { __NR_signalfd4, sys_signalfd4 }, +#endif +#ifdef CONFIG_EPOLL + { __NR_epoll_pwait, sys_epoll_pwait }, + { __NR_epoll_pwait2, sys_epoll_pwait2 }, +#endif + { -1, NULL }, +}; diff --git a/arch/loongarch/ow_syscall/systable.h b/arch/loongarch/ow_syscall/systable.h new file mode 100644 index 00000000000000..f2b6602cc7ef4f --- /dev/null +++ b/arch/loongarch/ow_syscall/systable.h @@ -0,0 +1,8 @@ +struct syscall_replace_table { + long syscall_num; + void *symbol_addr; + void *orig; +}; + +extern const char *sys_call_table_name[]; +extern struct syscall_replace_table syscall_to_replace[]; From a89b3aa52818fb69c6ae1697dec5030f8c6854b8 Mon Sep 17 00:00:00 2001 From: Tianhao Chai Date: Thu, 18 Jan 2024 18:07:58 -0500 Subject: [PATCH 457/521] AOSCOS: la_ow_syscall: add kconfig for module Signed-off-by: Tianhao Chai [Xi Ruoyao: Select KALLSYMS and KPROBE] Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/Kconfig | 2 ++ arch/loongarch/ow_syscall/Kconfig | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 arch/loongarch/ow_syscall/Kconfig diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 606597da46b8df..5a7d2e12beea48 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -820,3 +820,5 @@ source "drivers/cpufreq/Kconfig" endmenu source "arch/loongarch/kvm/Kconfig" + +source "arch/loongarch/ow_syscall/Kconfig" diff --git a/arch/loongarch/ow_syscall/Kconfig b/arch/loongarch/ow_syscall/Kconfig new file mode 100644 index 00000000000000..8ef159cf10557c --- /dev/null +++ b/arch/loongarch/ow_syscall/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Old-world system call compat +# + +config LOONGARCH_OW_SYSCALL + tristate "Old-world system call compatibility module" + depends on m + select KALLSYMS + select KPROBES + help + Say M to compile support for old-world system calls + as a module. This is needed to run liblol and programs + compiled for old world. + + If unsure, say N. + From 0538e1e467c220d5491d28c49eea59c4a55c0c7f Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 15 Oct 2024 20:32:21 +0800 Subject: [PATCH 458/521] AOSCOS: Revert "rcu: Fix rcu_barrier() VS post CPUHP_TEARDOWN_CPU invocation" When this change was introduced between v6.10.4 and v6.10.5, the Broadcom Tigon3 Ethernet interface (tg3) found on Apple MacBook Pro (15'', Mid 2010) would throw many rcu stall errors during boot up, causing peripherals such as the wireless card to misbehave. [ 24.153855] rcu: INFO: rcu_preempt detected expedited stalls on CPUs/tasks: { 2-.... } 21 jiffies s: 973 root: 0x4/. [ 24.166938] rcu: blocking rcu_node structures (internal RCU debug): [ 24.177800] Sending NMI from CPU 3 to CPUs 2: [ 24.183113] NMI backtrace for cpu 2 [ 24.183119] CPU: 2 PID: 1049 Comm: NetworkManager Not tainted 6.10.5-aosc-main #1 [ 24.183123] Hardware name: Apple Inc. MacBookPro6,2/Mac-F22586C8, BIOS MBP61.88Z.005D.B00.1804100943 04/10/18 [ 24.183125] RIP: 0010:__this_module+0x2d3d1/0x4f310 [tg3] [ 24.183135] Code: c3 cc cc cc cc 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 89 f6 48 03 77 30 8b 06 <31> f6 31 ff c3 cc cc cc cc 66 0f 1f 44 00 00 90 90 90 90 90 90 90 [ 24.183138] RSP: 0018:ffffbf1a011d75e8 EFLAGS: 00000082 [ 24.183141] RAX: 0000000000000000 RBX: ffffa04ec78f8a00 RCX: 0000000000000000 [ 24.183143] RDX: 0000000000000000 RSI: ffffbf1a00fb007c RDI: ffffa04ec78f8a00 [ 24.183145] RBP: 0000000000000b50 R08: 0000000000000000 R09: 0000000000000000 [ 24.183147] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000216 [ 24.183148] R13: ffffbf1a011d7624 R14: ffffa04ec78f8a08 R15: ffffa04ec78f8b40 [ 24.183151] FS: 00007f4c524b2140(0000) GS:ffffa05007d00000(0000) knlGS:0000000000000000 [ 24.183153] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 24.183155] CR2: 00007f7025eae3e8 CR3: 00000001040f8000 CR4: 00000000000006f0 [ 24.183157] Call Trace: [ 24.183162] [ 24.183167] ? nmi_cpu_backtrace+0xbf/0x140 [ 24.183175] ? nmi_cpu_backtrace_handler+0x11/0x20 [ 24.183181] ? nmi_handle+0x61/0x160 [ 24.183186] ? default_do_nmi+0x42/0x110 [ 24.183191] ? exc_nmi+0x1bd/0x290 [ 24.183194] ? end_repeat_nmi+0xf/0x53 [ 24.183203] ? __this_module+0x2d3d1/0x4f310 [tg3] [ 24.183207] ? __this_module+0x2d3d1/0x4f310 [tg3] [ 24.183210] ? __this_module+0x2d3d1/0x4f310 [tg3] [ 24.183213] [ 24.183214] [ 24.183215] __this_module+0x31828/0x4f310 [tg3] [ 24.183218] ? __this_module+0x2d390/0x4f310 [tg3] [ 24.183221] __this_module+0x398e6/0x4f310 [tg3] [ 24.183225] __this_module+0x3baf8/0x4f310 [tg3] [ 24.183229] __this_module+0x4733f/0x4f310 [tg3] [ 24.183233] ? _raw_spin_unlock_irqrestore+0x25/0x70 [ 24.183237] ? __this_module+0x398e6/0x4f310 [tg3] [ 24.183241] __this_module+0x4b943/0x4f310 [tg3] [ 24.183244] ? delay_tsc+0x89/0xf0 [ 24.183249] ? preempt_count_sub+0x51/0x60 [ 24.183254] __this_module+0x4be4b/0x4f310 [tg3] [ 24.183258] __dev_open+0x103/0x1c0 [ 24.183265] __dev_change_flags+0x1bd/0x230 [ 24.183269] ? rtnl_getlink+0x362/0x400 [ 24.183276] dev_change_flags+0x26/0x70 [ 24.183280] do_setlink+0xe16/0x11f0 [ 24.183286] ? __nla_validate_parse+0x61/0xd40 [ 24.183295] __rtnl_newlink+0x63d/0x9f0 [ 24.183301] ? kmem_cache_alloc_node_noprof+0x12b/0x360 [ 24.183308] ? kmalloc_trace_noprof+0x11e/0x350 [ 24.183312] ? rtnl_newlink+0x2e/0x70 [ 24.183316] rtnl_newlink+0x47/0x70 [ 24.183320] rtnetlink_rcv_msg+0x152/0x400 [ 24.183324] ? __netlink_sendskb+0x68/0x90 [ 24.183329] ? netlink_unicast+0x237/0x290 [ 24.183333] ? __pfx_rtnetlink_rcv_msg+0x10/0x10 [ 24.183336] netlink_rcv_skb+0x5b/0x110 [ 24.183343] netlink_unicast+0x1a4/0x290 [ 24.183347] netlink_sendmsg+0x222/0x4a0 [ 24.183350] ? proc_get_long.constprop.0+0x116/0x210 [ 24.183358] ____sys_sendmsg+0x379/0x3b0 [ 24.183363] ? copy_msghdr_from_user+0x6d/0xb0 [ 24.183368] ___sys_sendmsg+0x86/0xe0 [ 24.183372] ? addrconf_sysctl_forward+0xf3/0x270 [ 24.183378] ? _copy_from_iter+0x8b/0x570 [ 24.183384] ? __pfx_addrconf_sysctl_forward+0x10/0x10 [ 24.183388] ? _raw_spin_unlock+0x19/0x50 [ 24.183392] ? proc_sys_call_handler+0xf3/0x2f0 [ 24.183397] ? trace_hardirqs_on+0x29/0x90 [ 24.183401] ? __fdget+0xc2/0xf0 [ 24.183405] __sys_sendmsg+0x5b/0xc0 [ 24.183410] ? syscall_trace_enter+0x110/0x1b0 [ 24.183416] do_syscall_64+0x64/0x150 [ 24.183423] entry_SYSCALL_64_after_hwframe+0x76/0x7e I have bisected the error to this commit. Reverting it caused no new or perceivable issues on both the MacBook and a Zen4-based laptop. Revert this commit as a workaround. This reverts commit aa162aa4aa383a0a714b1c36e8fcc77612ddd1a2. Upstream report: https://bugzilla.kernel.org/show_bug.cgi?id=219390 Signed-off-by: Mingcong Bai Bug: https://lore.kernel.org/all/b8da4aec-4cca-4eb0-ba87-5f8641aa2ca9@leemhuis.info/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- kernel/rcu/tree.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 55df6d37145e87..06496a92efe4f9 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4452,15 +4452,11 @@ void rcutree_migrate_callbacks(int cpu) struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); bool needwake; - if (rcu_rdp_is_offloaded(rdp)) - return; - - raw_spin_lock_irqsave(&rcu_state.barrier_lock, flags); - if (rcu_segcblist_empty(&rdp->cblist)) { - raw_spin_unlock_irqrestore(&rcu_state.barrier_lock, flags); + if (rcu_rdp_is_offloaded(rdp) || + rcu_segcblist_empty(&rdp->cblist)) return; /* No callbacks to migrate. */ - } + raw_spin_lock_irqsave(&rcu_state.barrier_lock, flags); WARN_ON_ONCE(rcu_rdp_cpu_online(rdp)); rcu_barrier_entrain(rdp); my_rdp = this_cpu_ptr(&rcu_data); From 5a8a4abc7fc6b13f97713c2e939e16db9ce384a1 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Fri, 18 Oct 2024 20:51:39 +0800 Subject: [PATCH 459/521] AOSCOS: MIPS: Temporarily disable Loongson3 LL/SC errata check To workaround the following errors: LLSCCHK vmlinux ffffffff848a10c0: Branch target not a sync (lots of these errors) loongson3-llsc-check returns failure Fixes: e4acfbc18fc9 ("MIPS: Check Loongson3 LL/SC errata workaround correctness") Signed-off-by: Jiaxun Yang Link: https://t.me/c/1109254909/582977 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Makefile.postlink | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/mips/Makefile.postlink b/arch/mips/Makefile.postlink index ea0add7d56b23a..2543679da14924 100644 --- a/arch/mips/Makefile.postlink +++ b/arch/mips/Makefile.postlink @@ -24,9 +24,6 @@ quiet_cmd_relocs = RELOCS $@ vmlinux vmlinux.unstripped: FORCE @true -ifeq ($(CONFIG_CPU_LOONGSON3_WORKAROUNDS),y) - $(call if_changed,ls3_llsc) -endif ifeq ($(CONFIG_RELOCATABLE),y) $(call if_changed,relocs) endif From 575b094cd265784620581050412a77f294941de9 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sat, 2 Nov 2024 22:25:37 +0800 Subject: [PATCH 460/521] AOSCOS: platform/x86: hp-wmi: Mark 8BAB board for OMEN thermal profile Similar to 3a057bf30e044a51af8e6a8fe8cbfac49e2b9bc5 ("platform/x86: hp-wmi: Add thermal profile support for 8BAD boards"), the HP OMEN 9 2023 (8BAB) board (China-specific model) should also be added to the list of boards handled by the OMEN thermal profiles. Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/613200 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai [Mingcong Bai: (6.19) Resolved minor merge conflicts in drivers/platform/x86/hp/hp-wmi.c] Signed-off-by: Mingcong Bai [Mingcong Bai: Fixed a minor post-7.0-rc2 merge conflict in drivers/platform/x86/hp/hp-wmi.c] Signed-off-by: Mingcong Bai --- drivers/platform/x86/hp/hp-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index f63bc00d9a9b3e..3fba59462b450a 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -158,7 +158,7 @@ static const char * const omen_thermal_profile_boards[] = { "88F7", "88FD", "88FE", "88FF", "8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB", "8A15", "8A42", - "8BAD", + "8BAB", "8BAD", "8C58", "8E41", }; @@ -178,7 +178,7 @@ static const char * const omen_thermal_profile_force_v0_boards[] = { */ static const char * const omen_timed_thermal_profile_boards[] = { "8A15", "8A42", - "8BAD", + "8BAB", "8BAD", }; /* DMI Board names of Victus 16-d laptops */ From 63a401f316abf63c137562a3462dd258a34c136e Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 24 Oct 2024 20:53:23 +0800 Subject: [PATCH 461/521] AOSCOS: drm: amdgpu: disable ABM (Adaptive Backlight Management) by default Per report from a contributor, since v6.9, ABM was set to be automatically enabled/disabled. It may have seemed like a good idea for brighter screens, but the brightness adjustment is quite abrupt, which is quite bothersome. Disable this feature by default for now. Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/594612 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 60debd543e44ee..26b48c0100978b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -881,9 +881,9 @@ module_param_named(visualconfirm, amdgpu_dc_visual_confirm, uint, 0444); * Defaults to -1, or auto. Userspace can only override this level after * boot if it's set to auto. */ -int amdgpu_dm_abm_level = -1; +int amdgpu_dm_abm_level = 0; MODULE_PARM_DESC(abmlevel, - "ABM level (0 = off, 1-4 = backlight reduction level, -1 auto (default))"); + "ABM level (0 = off (default), 1-4 = backlight reduction level, -1 auto)"); module_param_named(abmlevel, amdgpu_dm_abm_level, int, 0444); int amdgpu_backlight = -1; From 14b2d1ec8f64c21dfaf97959d9e08eec8af9cfe5 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Tue, 22 Oct 2024 12:55:46 +0100 Subject: [PATCH 462/521] AOSCOS: MIPS: Check address space in ADE Signed-off-by: Jiaxun Yang Bug: https://t.me/c/1109254909/588345 Link: https://t.me/c/1109254909/588669 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/unaligned.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index db652c99b72e32..4f2055afbd7e83 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -1514,14 +1514,37 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) force_sig(SIGILL); } +static inline bool addr_legal(unsigned long addr, bool user) +{ + if (addr < TASK_SIZE) + return true; + + if (user) + return false; + + if (addr >= CKSEG3) + return true; + +#ifdef CONFIG_64BIT + if (addr >= XKPHYS && addr < CKSSEG) + return true; +#else + if (addr >= CKSEG0 && addr < CKSEG2) + return true; +#endif + + return false; +} + asmlinkage void do_ade(struct pt_regs *regs) { enum ctx_state prev_state; unsigned int *pc; prev_state = exception_enter(); - perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, - 1, regs, regs->cp0_badvaddr); + + if (!addr_legal(regs->cp0_badvaddr, user_mode(regs))) + goto sigbus; #ifdef CONFIG_64BIT /* @@ -1545,6 +1568,9 @@ asmlinkage void do_ade(struct pt_regs *regs) if (regs->cp0_badvaddr == regs->cp0_epc) goto sigbus; + perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, + 1, regs, regs->cp0_badvaddr); + if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) goto sigbus; if (unaligned_action == UNALIGNED_ACTION_SIGNAL) From 51e059ed012747bf719d2cd0a7f4a8045c690059 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Thu, 21 Nov 2024 18:53:37 +0800 Subject: [PATCH 463/521] AOSCOS: remove dependencies on UBUNTU_ODM_DRIVERS That specific config option doesn't exist in AOSC OS. Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1912789 Co-authored-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpio/Kconfig | 1 - drivers/hwmon/Kconfig | 1 - drivers/leds/Kconfig | 1 - drivers/mfd/Kconfig | 1 - 4 files changed, 4 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 254bd37f75acf8..650ee3f44c672c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1789,7 +1789,6 @@ menu "PCI GPIO expanders" config GPIO_AAEON tristate "AAEON GPIO support" depends on ASUS_WMI - depends on UBUNTU_ODM_DRIVERS select MFD_AAEON help Say yes here to support GPIO pins on Single Board Computers produced diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index cc06e212d21a69..d0dc67cef19ed8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -41,7 +41,6 @@ comment "Native drivers" config SENSORS_AAEON tristate "AAEON hwmon driver" depends on X86 - depends on UBUNTU_ODM_DRIVERS select MFD_AAEON help This hwmon driver adds support for reporting temperature or fan diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 7a1ad03e1db2c8..646f1836005d96 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -75,7 +75,6 @@ config LEDS_88PM860X config LEDS_AAEON tristate "AAEON LED driver" depends on X86 - depends on UBUNTU_ODM_DRIVERS select MFD_AAEON help This led driver adds support for LED brightness control on Single diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1463c0b0a55d3b..b596ce54a6645c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2405,7 +2405,6 @@ config MFD_QCOM_PM8008 config MFD_AAEON tristate "AAEON WMI MFD devices" depends on ASUS_WMI - depends on UBUNTU_ODM_DRIVERS help Say yes here to support mltiple IO devices on Single Board Computers produced by AAEON. From 36a7cf3abc70356b250dfd466c775da1c11d9d2a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 12 Jan 2025 15:43:08 +0800 Subject: [PATCH 464/521] AOSCOS: kvm: disable enable_virt_at_load by default Disable enable_virt_at_load, a >= 6.12 on-by-default parameter introduced with commit b4886fab6fb6 ("KVM: Add a module param to allow enabling virtualization when KVM is loaded"). By enabling this paramter, VirtualBox (as of 7.1.4) will fail to launch any virtualised systems with the following error: VirtualBox can't operate in VMX root mode. Please disable the KVM kernel extension, recompile your kernel and reboot (VERR_VMX_IN_VMX_ROOT_MODE). The original commit indicated that this was introduced to reduce latency during KVM initialisation and that it "could be problematic for use case that need to spin up VMs quickly." An optimisation that is more than likely unnecessary for our (predominantly desktop-oriented) crowd. Bottom line - make VirtualBox work again (until they come up with a fix). Fixes: b4886fab6fb6 ("KVM: Add a module param to allow enabling virtualization when KVM is loaded") Reported-by: xtex Link: https://github.com/AOSC-Dev/aosc-os-abbs/issues/8897 Link: https://lore.kernel.org/kvm/ZwQjUSOle6sWARsr@google.com/T/ Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/715020 Acked-by: xtex Reviewed-by: xtex Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- virt/kvm/kvm_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 881f92d7a469e7..50511430f0f854 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -5568,7 +5568,7 @@ static struct miscdevice kvm_dev = { }; #ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING -bool __ro_after_init enable_virt_at_load = true; +bool __ro_after_init enable_virt_at_load = false; module_param(enable_virt_at_load, bool, 0444); EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_virt_at_load); From e63b9362f9f0deb615aafb9505a1247a983cc608 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sat, 25 Jan 2025 03:03:03 +0800 Subject: [PATCH 465/521] AOSCOS: MIPS: loongson64: deselect ARCH_SUPPORTS_HUGETLBFS Disable HugeTLB support as it causes TLB exceptions and illegal memory access on 3A4000 and 3B4000 (possibly more, but we haven't tested other platforms yet). Suggested-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 3799140b031a7f..2f8402b01c41c6 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -26,7 +26,7 @@ config MIPS select ARCH_USE_MEMTEST select ARCH_USE_QUEUED_RWLOCKS select ARCH_USE_QUEUED_SPINLOCKS - select ARCH_SUPPORTS_HUGETLBFS if CPU_SUPPORTS_HUGEPAGES + select ARCH_SUPPORTS_HUGETLBFS if CPU_SUPPORTS_HUGEPAGES && !CPU_LOONGSON64 select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WANT_LD_ORPHAN_WARN From b6082a6b9e657cbae524c1d4161ded727e206b94 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sat, 25 Jan 2025 13:00:30 +0800 Subject: [PATCH 466/521] AOSCOS: MIPS: platform: disable unreliable CSR-based temperature reading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable CSR-based temperature reading, found only on Loongson 3A/B4000 processors. We found that on the Excelsior E23L (3B4000) boards, the temperature reading would erratically pin to 239°C, triggering immediate thermal shutdown. We don't yet know about the true cause behind this issue, as both boards we own are indeed using non-original heatsinks (but they can in fact sustain >= 6 hours of stress testing with s-tui). For now, disable this feature so that we can keep those systems working and Loongson 3A/B4000 can still report their temperatures via the Chip Temperature register (loongson_chiptemp). Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/736238 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/mips/cpu_hwmon.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/platform/mips/cpu_hwmon.c b/drivers/platform/mips/cpu_hwmon.c index 2ac2f31090f96f..93619227f0396e 100644 --- a/drivers/platform/mips/cpu_hwmon.c +++ b/drivers/platform/mips/cpu_hwmon.c @@ -135,9 +135,22 @@ static int __init loongson_hwmon_init(void) { pr_info("Loongson Hwmon Enter...\n"); + /* + * FIXME: Disable CSR-based temperature reading, found only on Loongson + * 3A/B4000 processors. We found that on the Excelsior E23L (3B4000) + * boards, the temperature reading would erratically pin to 239°C, + * triggering immediate thermal shutdown. + * + * We don't yet know about the true cause behind this issue, as both + * boards we own are indeed using non-original heatsinks (but they can + * in fact sustain >= 6 hours of stress testing with s-tui). + * + * For now, disable this feature so that we can keep those systems + * working and Loongson 3A/B4000 can still report their temperatures + * via the Chip Temperature register (loongson_chiptemp). + */ if (cpu_has_csr()) - csr_temp_enable = csr_readl(LOONGSON_CSR_FEATURES) & - LOONGSON_CSRF_TEMP; + csr_temp_enable = 0; if (!csr_temp_enable && !loongson_chiptemp[0]) return -ENODEV; From 393103fa50cc324499eab9e77d03648e8d4895fb Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 24 Feb 2025 14:01:03 +0800 Subject: [PATCH 467/521] AOSCOS: drm/radeon: limit mmiowb() hack for radeon_ring_commit() to MACH_LOONGSON64 As mentioned in the previous patch ("LOONGARCH64: FROMLIST: drm/radeon: Call mmiowb() at the end of radeon_ring_commit()") that attempts to fix improper removal of all `mmiowb()' calls in commit fb24ea52f78e ("drivers: Remove explicit invocations of mmiowb()"), the the wptr needs to be more effectively updated on weak ordering architectures such as LoongArch, as the `radeon_ring_commit()' was protected by a mutex. To quote Huacai Chen: "The mmio in radeon_ring_commit() is protected by a mutex rather than a spinlock, but in the mutex fastpath it behaves similar to spinlock and need a mmiowb() to make sure the wptr is up-to-date for hardware." From what could be seen in arch/, `mmiowb()' is only implemented for loongarch, mips, powerpc, riscv64, s390, and sh. With regards to the analysis above, this hack should technically be enabled for all the aforementioned architecture/machine with PCI support. However, according to our testing, this hack is only needed for LoongArch and Loongson 3 (MIPS) platforms based on the 7A bridge chip. Limit this hack to MACH_LOONGSON64 which matches this description to free the previous patch from a .loongarch64 suffix in AOSC OS, easing patchset maintenance. Fixes: "FROMLIST: drm/radeon: Call mmiowb() at the end of radeon_ring_commit()" Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/768003 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/radeon/radeon_ring.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 346f0e49bb2eaa..92aa8e1364c319 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -185,7 +185,9 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring, if (hdp_flush && rdev->asic->mmio_hdp_flush) rdev->asic->mmio_hdp_flush(rdev); radeon_ring_set_wptr(rdev, ring); +#if defined(CONFIG_MACH_LOONGSON64) mmiowb(); /* Make sure wptr is up-to-date for hw */ +#endif } /** From 49d2ff9ffb6a5eab6d9f07e3978990355cb8035b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Feb 2025 11:43:02 +0800 Subject: [PATCH 468/521] AOSCOS: USB: core: only enable root_hub wakeup on MACH_LOONGSON64 The previous attempt to fix USB remote wake on Loongson 7A platforms broke S3 suspend on at least the following device(s): - Lenovo ThinkPad P14s Gen 1 (20Y1CTO1WW) Where the device would wake up immediately after entering S3 suspend. Since this fix was really only intended as a quirk for Loongson 7A platforms, guard the additional calls to `usb_enable_remote_wakeup()' and `usb_disable_remote_wakeup()' within `CONFIG_MACH_LOONGSON64', corresponding to all 64-bit Loongson platforms (unless this also breaks non-7A devices, in which case we will tighten the conditions further). Fixes: "FROMLIST: USB: core: Enable root_hub's remote wakeup for wakeup sources" Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/816220 Link: https://t.me/c/1109254909/820298 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/usb/core/hub.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index e90a72bd1cc348..69b5f376889b33 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3523,7 +3523,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (PMSG_IS_AUTO(msg)) goto err_wakeup; } +#if defined(CONFIG_MACH_LOONGSON64) usb_enable_remote_wakeup(udev->bus->root_hub); +#endif } /* disable USB2 hardware LPM */ @@ -3589,7 +3591,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->do_remote_wakeup) { (void) usb_disable_remote_wakeup(udev); +#if defined(CONFIG_MACH_LOONGSON64) (void) usb_disable_remote_wakeup(udev->bus->root_hub); +#endif } err_wakeup: From b8859edf8c5ccbfd4da7d14c958c1d10ddd39756 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Thu, 27 Mar 2025 13:53:42 +0800 Subject: [PATCH 469/521] AOSCOS: net/phytmac: include linux/vmalloc.h to fix build errors Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 9895b3fccd26e3..e9bf8ec24fa577 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_ptp.h" From 9816275600969e6d46ae11de74054fdca0313133 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Wed, 9 Apr 2025 00:10:16 +0800 Subject: [PATCH 470/521] AOSCOS: bpftool: install into bin instead of sbin Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- tools/bpf/bpftool/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 0febf60e1b6465..f7b90e64ce64dc 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -297,8 +297,8 @@ clean: $(LIBBPF)-clean $(LIBBPF_BOOTSTRAP)-clean feature-detect-clean install-bin: $(OUTPUT)bpftool $(call QUIET_INSTALL, bpftool) - $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/sbin - $(Q)$(INSTALL) $(OUTPUT)bpftool $(DESTDIR)$(prefix)/sbin/bpftool + $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/bin + $(Q)$(INSTALL) $(OUTPUT)bpftool $(DESTDIR)$(prefix)/bin/bpftool install: install-bin $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(bash_compdir) @@ -306,7 +306,7 @@ install: install-bin uninstall: $(call QUIET_UNINST, bpftool) - $(Q)$(RM) -- $(DESTDIR)$(prefix)/sbin/bpftool + $(Q)$(RM) -- $(DESTDIR)$(prefix)/bin/bpftool $(Q)$(RM) -- $(DESTDIR)$(bash_compdir)/bpftool doc: From 9239c646095201ff2de5e149f49d9dc2969ac30d Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sat, 12 Jul 2025 19:36:15 +0800 Subject: [PATCH 471/521] AOSCOS: net/phytmac: Replace xdp_do_flush_map() with xdp_do_flush() Per 7f04bd109d4c ("net: Tree wide: Replace xdp_do_flush_map() with xdp_do_flush()."): xdp_do_flush_map() is deprecated and new code should use xdp_do_flush() instead. Replace xdp_do_flush_map() with xdp_do_flush(). Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index e9bf8ec24fa577..3f1f2098284de9 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1251,7 +1251,7 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, } if (xdp_xmit & PHYTMAC_XDP_REDIR) - xdp_do_flush_map(); + xdp_do_flush(); phytmac_rx_clean(queue); From 7fa2fa40cc4330660362732cc567d4c71a0f3dcc Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sat, 12 Jul 2025 19:38:09 +0800 Subject: [PATCH 472/521] AOSCOS: net/phytmac: Remove unnecessary pcim_iounmap_regions() call pcim_iounmap_regions() is removed in commit 855c634930f0 ("PCI: Remove pcim_iounmap_regions()"). Moreover, it is not necessary to call it in the driver's remove() function, since it does cleanup automatically on driver detach. Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index fac5d717ad7669..69f34c2faaf5b0 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -177,7 +177,6 @@ static void phytmac_pci_remove(struct pci_dev *pdev) phytmac_free_pdata(pdata); bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); - pcim_iounmap_regions(pdev, bar_mask); pci_disable_device(pdev); } From be249e2fafcabe5dd0130c9a957af881fb4bc14e Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 13 Jul 2025 21:59:21 +0800 Subject: [PATCH 473/521] AOSCOS: net/phytium: Depends on ACPI and ARCH_PHYTIUM The driver seems relying on ACPI to power up the device. And the device is only available as a part of Phytium SoC. Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index e68ca5d82181e8..74f671b5e0d416 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -20,6 +20,8 @@ if NET_VENDOR_PHYTIUM config PHYTMAC tristate "Phytium GMAC support" + depends on ACPI + depends on ARCH_PHYTIUM depends on HAS_DMA select PHYLINK select CRC32 From bb3730044025a98565077ba01e017959042820ee Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Mon, 14 Jul 2025 18:06:42 +0800 Subject: [PATCH 474/521] AOSCOS: arm64: Disable MPAM by default We have to work OotB on W510, and ARM64 does not support extending the built-in command line with the one from the bootloader. So only try MPAM if the user explicitly gives "aosc.try_mpam=1" via the cmdline. Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/kernel/pi/idreg-override.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/kernel/pi/idreg-override.c b/arch/arm64/kernel/pi/idreg-override.c index bc57b290e5e7ba..2d45dbb6894760 100644 --- a/arch/arm64/kernel/pi/idreg-override.c +++ b/arch/arm64/kernel/pi/idreg-override.c @@ -215,6 +215,17 @@ static const struct ftr_set_desc sw_features __prel64_initconst = { }, }; +static struct arm64_ftr_override __read_mostly aosc_feature_override; + +static const struct ftr_set_desc aosc_features __prel64_initconst = { + .name = "aosc", + .override = &aosc_feature_override, + .fields = { + FIELD("try_mpam", 0, NULL), + {} + }, +}; + static const PREL64(const struct ftr_set_desc, reg) regs[] __prel64_initconst = { { &mmfr0 }, @@ -226,6 +237,7 @@ PREL64(const struct ftr_set_desc, reg) regs[] __prel64_initconst = { { &isar2 }, { &smfr0 }, { &sw_features }, + { &aosc_features }, }; static const struct { @@ -390,6 +402,14 @@ static __init void parse_cmdline(const void *fdt, int chosen) if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && prop) __parse_cmdline(prop, true); + + /* + * Sorry but we have to work OotB on some platforms with broken + * firmware, notably W510. Use "aosc.try_mpam=1" if you really need + * MPAM on AOSC. + */ + if (!arm64_apply_feature_override(0, 0, 4, &aosc_feature_override)) + __parse_cmdline("arm64.nompam", true); } void __init init_feature_override(u64 boot_status, const void *fdt, From fb08a9d692abc2eef80be6ec996f247501f8d3c7 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:43:40 +0800 Subject: [PATCH 475/521] MARKER: AOSCOS: Start of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 2f8402b01c41c6..93fc1dba4faf2d 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,6 +550,7 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC + depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From ef32c8e2d7593761abd2bbcd231213e92b74deee Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 29 Nov 2019 09:11:38 +0800 Subject: [PATCH 476/521] AOSCOS: MIPS: Loongson: Add constant timer support Partially implemented by commit 8267e78f020a ("MIPS: Tidy up CP0.Config6 bits definition"). [Mingcong Bai: Resolved merge conflicts in the following... arch/mips/include/asm/clocksource.h arch/mips/include/asm/cpu-features.h arch/mips/include/asm/cpu.h arch/mips/include/asm/mach-loongson64/loongson_regs.h arch/mips/include/asm/mipsregs.h arch/mips/include/asm/vdso/gettimeofday.h arch/mips/kernel/cpu-probe.c arch/mips/loongson64/constant_timer.c arch/mips/loongson64/loongson-3/Makefile => arch/mips/loongson64/Makefile arch/mips/loongson64/smp.c] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/include/asm/cpu-features.h | 4 + arch/mips/include/asm/cpu.h | 1 + .../asm/mach-loongson64/loongson_regs.h | 30 ++ arch/mips/include/asm/mipsregs.h | 1 + arch/mips/include/asm/time.h | 20 +- arch/mips/include/asm/vdso/clocksource.h | 3 +- arch/mips/include/asm/vdso/gettimeofday.h | 20 ++ arch/mips/kernel/cpu-probe.c | 16 ++ arch/mips/loongson64/Makefile | 1 + arch/mips/loongson64/constant_timer.c | 256 ++++++++++++++++++ arch/mips/loongson64/smp.c | 4 +- 11 files changed, 352 insertions(+), 4 deletions(-) create mode 100644 arch/mips/loongson64/constant_timer.c diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index 3f11e5218e6c65..cbc1795e204779 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -658,6 +658,10 @@ # define cpu_has_mm_full __opt(MIPS_CPU_MM_FULL) #endif +#ifndef cpu_has_constant_timer +# define cpu_has_constant_timer __opt(MIPS_CPU_CONST_TIMER) +#endif + /* * Guest capabilities */ diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h index 0fd9f9bbd21f1d..05e6ce0bb3fb75 100644 --- a/arch/mips/include/asm/cpu.h +++ b/arch/mips/include/asm/cpu.h @@ -420,6 +420,7 @@ enum cpu_type_enum { #define MIPS_CPU_MAC_2008_ONLY BIT_ULL(60) /* CPU Only support MAC2008 Fused multiply-add instruction */ #define MIPS_CPU_FTLBPAREX BIT_ULL(61) /* CPU has FTLB parity exception */ #define MIPS_CPU_GSEXCEX BIT_ULL(62) /* CPU has GSExc exception */ +#define MIPS_CPU_CONST_TIMER BIT_ULL(63) /* CPU has constant timer */ /* * CPU ASE encodings diff --git a/arch/mips/include/asm/mach-loongson64/loongson_regs.h b/arch/mips/include/asm/mach-loongson64/loongson_regs.h index fec76750760492..47294ab78a2cd3 100644 --- a/arch/mips/include/asm/mach-loongson64/loongson_regs.h +++ b/arch/mips/include/asm/mach-loongson64/loongson_regs.h @@ -131,6 +131,9 @@ static inline u32 read_cpucfg(u32 reg) #define LOONGSON_CFG7_GCCAEQRP BIT(0) #define LOONGSON_CFG7_UCAWINP BIT(1) +#define CSR_TO_RAW_ADDR(cpu, csr) ((0x900000003ff00000 | csr | \ + (((u64)cpu & 0xc) << 42)) | (((u64)cpu & 0x3) << 8)) + static inline bool cpu_has_csr(void) { if (cpu_has_cfg()) @@ -247,6 +250,12 @@ static inline void csr_writeq(u64 val, u32 reg) #define CSR_MAIL_SEND_BUF_SHIFT 32 #define CSR_MAIL_SEND_H32_MASK 0xFFFFFFFF00000000ULL +#define LOONGSON_CSR_TIMER_CFG 0x1060 +#define LOONGSON_CSR_TIMER_TICK 0x1070 +#define CONSTANT_TIMER_CFG_PERIODIC (_ULCAST_(1) << 62) +#define CONSTANT_TIMER_CFG_EN (_ULCAST_(1) << 61) +#define CONSTANT_TIMER_INITVAL_RESET (_ULCAST_(0xffff) << 48) + static inline u64 drdtime(void) { int rID = 0; @@ -265,4 +274,25 @@ static inline u64 drdtime(void) return val; } +static inline unsigned int calc_const_freq(void) +{ + unsigned int res; + unsigned int base_freq; + unsigned int cfm, cfd; + + res = read_cpucfg(LOONGSON_CFG2); + if (!(res & LOONGSON_CFG2_LLFTP)) + return 0; + + res = read_cpucfg(LOONGSON_CFG5); + cfm = res & 0xffff; + cfd = (res >> 16) & 0xffff; + base_freq = read_cpucfg(LOONGSON_CFG4); + + if (!base_freq || !cfm || !cfd) + return 0; + else + return (base_freq * cfm / cfd); +} + #endif diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 12a095dbf9e2a9..805efd3ea9697d 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -838,6 +838,7 @@ #define MTI_CONF6_SYND (_ULCAST_(1) << 13) /* Sleep state performance counter disable */ #define MTI_CONF6_SPCD (_ULCAST_(1) << 14) + /* proAptiv FTLB on/off bit */ #define MTI_CONF6_FTLBEN (_ULCAST_(1) << 15) /* Disable load/store bonding */ diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 93f8cd52e3d471..72fec943a9e442 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -42,11 +42,19 @@ extern int __weak get_c0_perfcount_int(void); */ extern unsigned int get_c0_compare_int(void); extern int r4k_clockevent_init(void); +#ifdef CONFIG_CPU_LOONGSON64 +extern int constant_clockevent_init(void); +#else +static inline int constant_clockevent_init(void) { return 0; } +#endif static inline int mips_clockevent_init(void) { #ifdef CONFIG_CEVT_R4K - return r4k_clockevent_init(); + if (!cpu_has_constant_timer) + return r4k_clockevent_init(); + else + return constant_clockevent_init(); #else return -ENXIO; #endif @@ -56,11 +64,19 @@ static inline int mips_clockevent_init(void) * Initialize the count register as a clocksource */ extern int init_r4k_clocksource(void); +#ifdef CONFIG_CPU_LOONGSON64 +extern int init_constant_clocksource(void); +#else +static inline int init_constant_clocksource(void) { return 0; } +#endif static inline __init int init_mips_clocksource(void) { #ifdef CONFIG_CSRC_R4K - return init_r4k_clocksource(); + if (!cpu_has_constant_timer) + return init_r4k_clocksource(); + else + return init_constant_clocksource(); #else return 0; #endif diff --git a/arch/mips/include/asm/vdso/clocksource.h b/arch/mips/include/asm/vdso/clocksource.h index 510e1671d89851..7fd43ca06eb117 100644 --- a/arch/mips/include/asm/vdso/clocksource.h +++ b/arch/mips/include/asm/vdso/clocksource.h @@ -4,6 +4,7 @@ #define VDSO_ARCH_CLOCKMODES \ VDSO_CLOCKMODE_R4K, \ - VDSO_CLOCKMODE_GIC + VDSO_CLOCKMODE_GIC, \ + VDSO_CLOCKMODE_CONST #endif /* __ASM_VDSOCLOCKSOURCE_H */ diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h index 32d2d173fdc0be..8cc0beef182d18 100644 --- a/arch/mips/include/asm/vdso/gettimeofday.h +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -183,6 +183,22 @@ static __always_inline u64 read_gic_count(const struct vdso_time_data *data) #endif +#ifdef CONFIG_CPU_LOONGSON64 +static __always_inline u64 read_const_count(void) +{ + unsigned long count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} +#endif + static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_time_data *vd) { @@ -193,6 +209,10 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, #ifdef CONFIG_CLKSRC_MIPS_GIC if (clock_mode == VDSO_CLOCKMODE_GIC) return read_gic_count(vd); +#endif +#ifdef CONFIG_CPU_LOONGSON64 + if (clock_mode == VDSO_CLOCKMODE_CONST) + return read_const_count(); #endif /* * Core checks mode already. So this raced against a concurrent diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 88bae0ca721ca8..3647315658b752 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -34,6 +34,10 @@ #include +#ifdef CONFIG_CPU_LOONGSON64 +#include +#endif + /* Hardware capabilities */ unsigned int elf_hwcap __read_mostly; EXPORT_SYMBOL_GPL(elf_hwcap); @@ -1691,6 +1695,17 @@ static inline void decode_cpucfg(struct cpuinfo_mips *c) c->ases |= MIPS_ASE_LOONGSON_CAM; } +static void decode_loongson_cpucfg(struct cpuinfo_mips *c) +{ +#ifdef CONFIG_CPU_LOONGSON64 + unsigned int cpucfg; + + cpucfg = read_cpucfg(LOONGSON_CFG2); + if (cpucfg & LOONGSON_CFG2_LLFTP) + c->options |= MIPS_CPU_CONST_TIMER; +#endif +} + static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) { c->cputype = CPU_LOONGSON64; @@ -1752,6 +1767,7 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) set_isa(c, MIPS_CPU_ISA_M64R2); __cpu_full_name[cpu] = "Loongson-3A R4 (Loongson-3A4000)"; decode_cpucfg(c); + decode_loongson_cpucfg(c); change_c0_config6(LOONGSON_CONF6_EXTIMER | LOONGSON_CONF6_INTIMER, LOONGSON_CONF6_INTIMER); break; diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index cbba30dfddf5d0..ba083beb6ca818 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ + constant_timer.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c new file mode 100644 index 00000000000000..b8bdbb2c47b513 --- /dev/null +++ b/arch/mips/loongson64/constant_timer.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int constant_timer_irq_installed; + +static DEFINE_SPINLOCK(state_lock); +DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device); + +static void constant_event_handler(struct clock_event_device *dev) +{ +} + +static irqreturn_t constant_timer_interrupt(int irq, void *data) +{ + int cpu = smp_processor_id(); + const int r2 = cpu_has_mips_r2_r6; + struct clock_event_device *cd; + + if ((cp0_perfcount_irq < 0) && perf_irq() == IRQ_HANDLED && !r2) + return IRQ_HANDLED; + + /* + * The same applies to performance counter interrupts. But with the + * above we now know that the reason we got here must be a timer + * interrupt. Being the paranoiacs we are we check anyway. + */ + if (!r2 || (read_c0_cause() & CAUSEF_TI)) { + /* Clear Count/Compare Interrupt */ + write_c0_compare(read_c0_compare()); + cd = &per_cpu(constant_clockevent_device, cpu); + cd->event_handler(cd); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static struct irqaction constant_timer_irqaction = { + .handler = constant_timer_interrupt, + .flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, + .name = "timer", +}; + +static int constant_set_state_oneshot(struct clock_event_device *evt) +{ + unsigned int raw_cpuid; + unsigned long cfg, addr; + + spin_lock(&state_lock); + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + if (!cpu_has_csr()) + cfg = readq((void *)addr); + else + cfg = csr_readq(LOONGSON_CSR_TIMER_CFG); + + /* set timer type + * 1 : periodic interrupt + * 0 : non-periodic(oneshot) interrupt + */ + cfg |= CONSTANT_TIMER_CFG_EN; + cfg &= ~CONSTANT_TIMER_CFG_PERIODIC; + + if (!cpu_has_csr()) + writeq(cfg, (void *)addr); + else + csr_writeq(cfg, LOONGSON_CSR_TIMER_CFG); + + spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) +{ + return 0; +} + +static int constant_set_state_periodic(struct clock_event_device *evt) +{ + unsigned int period; + unsigned int raw_cpuid; + unsigned long cfg, addr; + + spin_lock(&state_lock); + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + if (!cpu_has_csr()) + cfg = readq((void *)addr); + else + cfg = csr_readq(LOONGSON_CSR_TIMER_CFG); + + cfg &= CONSTANT_TIMER_INITVAL_RESET; + cfg |= (CONSTANT_TIMER_CFG_PERIODIC | CONSTANT_TIMER_CFG_EN); + + period = calc_const_freq(); + if (!period) + period = cpu_clock_freq; + + period = period / HZ; + + if (!cpu_has_csr()) + writeq(cfg | period, (void *)addr); + else + csr_writeq(cfg | period, LOONGSON_CSR_TIMER_CFG); + + spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_shutdown(struct clock_event_device *evt) +{ + return 0; +} + +static int constant_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned long addr; + unsigned int raw_cpuid; + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + writeq(delta | CONSTANT_TIMER_CFG_EN, (void *)addr); + + return 0; +} + +static int csr_constant_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + csr_writeq(delta | CONSTANT_TIMER_CFG_EN, LOONGSON_CSR_TIMER_CFG); + return 0; +} + +int constant_clockevent_init(void) +{ + unsigned int irq; + unsigned int config; + unsigned int const_freq; + unsigned long min_delta = 0x600; + unsigned long max_delta = (1UL << 48) - 1; + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd; + + config = read_c0_config6(); + config |= LOONGSON_CONF6_EXTIMER; + write_c0_config6(config); + + const_freq = calc_const_freq(); + if (!const_freq) + const_freq = cpu_clock_freq; + + irq = get_c0_compare_int(); + + cd = &per_cpu(constant_clockevent_device, cpu); + + cd->name = "Constant"; + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_PERCPU; + + cd->rating = 320; + cd->irq = irq; + cd->cpumask = cpumask_of(cpu); + cd->set_state_oneshot = constant_set_state_oneshot; + cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped; + cd->set_state_periodic = constant_set_state_periodic; + cd->set_state_shutdown = constant_set_state_shutdown; + if (!cpu_has_csr()) + cd->set_next_event = constant_next_event; + else + cd->set_next_event = csr_constant_next_event; + + cd->event_handler = constant_event_handler; + + clockevents_config_and_register(cd, const_freq, min_delta, max_delta); + + if (constant_timer_irq_installed) + return 0; + + constant_timer_irq_installed = 1; + setup_irq(irq, &constant_timer_irqaction); + + return 0; +} + +static u64 read_const_counter(struct clocksource *clk) +{ + u64 count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} + +static struct clocksource clocksource_const = { + .name = "Constant", + .rating = 400, + .read = read_const_counter, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .mult = 0, + .shift = 10, +}; + +static u64 read_sched_clock(void) +{ + u64 count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} + +int __init init_constant_clocksource(void) +{ + int res; + unsigned long freq; + + freq = calc_const_freq(); + if (freq) + freq = cpu_clock_freq; + + clocksource_const.mult = + clocksource_hz2mult(freq, clocksource_const.shift); + + res = clocksource_register_hz(&clocksource_const, freq); + + sched_clock_register(read_sched_clock, 64, freq); + clocksource_const.archdata.vdso_clock_mode = VDSO_CLOCKMODE_CONST; + + return res; +} diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index c4e97a59df3055..c30f83d58b37b9 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -435,7 +435,9 @@ static void loongson3_smp_finish(void) { int cpu = smp_processor_id(); - write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + if (!cpu_has_constant_timer) + write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + local_irq_enable(); ipi_clear_buf(cpu); From 09e41bcb61e6f7e02dacf5e7cd6714f87021cff7 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 3 Dec 2024 16:52:14 +0800 Subject: [PATCH 477/521] AOSCOS: MIPS: loongson64: fix constant timer build on kernel versions >= v5.7-rc2 With commit 07d8350ede4c ("genirq: Remove setup_irq() and remove_irq()"), setup_irq() was removed in favour of request_irq(). This was done following commit cc2550b421aa ("clocksource: Replace setup_irq() by request_irq()") (v5.7-rc1). Follow the latter commit as a model to revise loongson64's constant_timer and use request_irq() instead. Also add error handling and output for cases of failure. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/constant_timer.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c index b8bdbb2c47b513..975fb93c4efa2e 100644 --- a/arch/mips/loongson64/constant_timer.c +++ b/arch/mips/loongson64/constant_timer.c @@ -42,12 +42,6 @@ static irqreturn_t constant_timer_interrupt(int irq, void *data) return IRQ_NONE; } -static struct irqaction constant_timer_irqaction = { - .handler = constant_timer_interrupt, - .flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, - .name = "timer", -}; - static int constant_set_state_oneshot(struct clock_event_device *evt) { unsigned int raw_cpuid; @@ -155,6 +149,7 @@ int constant_clockevent_init(void) unsigned long max_delta = (1UL << 48) - 1; unsigned int cpu = smp_processor_id(); struct clock_event_device *cd; + int err; config = read_c0_config6(); config |= LOONGSON_CONF6_EXTIMER; @@ -192,7 +187,14 @@ int constant_clockevent_init(void) return 0; constant_timer_irq_installed = 1; - setup_irq(irq, &constant_timer_irqaction); + + err = request_irq(irq, constant_timer_interrupt, + IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, + "timer", cd); + if (err) { + pr_err("loongson64: setup irq for constant_clock_event failed: %d\n", err); + return err; + } return 0; } From c06be77a58ffc906c089898bd020427b3b3f0333 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 3 Dec 2024 16:56:36 +0800 Subject: [PATCH 478/521] AOSCOS: MIPS: loongson64: use generic vDSO clock mode storage for constant_timer Follow commit e1bdb22ebe53 ("mips: vdso: Use generic VDSO clock mode storage") (v5.7-rc1) and revise vdso_clock_mode setting in constant_timer code for loongson64 platforms. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/constant_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c index 975fb93c4efa2e..c1fda4a3ae1657 100644 --- a/arch/mips/loongson64/constant_timer.c +++ b/arch/mips/loongson64/constant_timer.c @@ -252,7 +252,7 @@ int __init init_constant_clocksource(void) res = clocksource_register_hz(&clocksource_const, freq); sched_clock_register(read_sched_clock, 64, freq); - clocksource_const.archdata.vdso_clock_mode = VDSO_CLOCKMODE_CONST; + clocksource_const.vdso_clock_mode = VDSO_CLOCKMODE_CONST; return res; } From fb3a33930bcb5cfee8c0dbe75539e22c4ac576d5 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 479/521] AOSCOS: MIPS: Loongson: Add PMON read/write in OS support PMON is the firmware(BIOS) of Loongson. Usage: modprobe mtd modprobe mtdblock modprobe pmon_flash dd if=pmon.bin of=/dev/mtdblcok0 Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/mips/Makefile | 4 ++ drivers/platform/mips/pmon_flash.c | 100 +++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 drivers/platform/mips/pmon_flash.c diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 4c71444e453a6c..4dbbc9bd9d87fd 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -2,3 +2,7 @@ obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o obj-$(CONFIG_RS780E_ACPI) += rs780e-acpi.o obj-$(CONFIG_LS2K_RESET) += ls2k-reset.o + +ifdef CONFIG_MTD +obj-m += pmon_flash.o +endif diff --git a/drivers/platform/mips/pmon_flash.c b/drivers/platform/mips/pmon_flash.c new file mode 100644 index 00000000000000..8fd5f4bf8b1bb3 --- /dev/null +++ b/drivers/platform/mips/pmon_flash.c @@ -0,0 +1,100 @@ +/* + * Copyright www.lemote.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FLASH_PHYS_ADDR 0x1fc00000 +#define FLASH_SIZE 0x100000 + +#define FLASH_PARTITION0_ADDR 0x00000000 +#define FLASH_PARTITION0_SIZE 0x00100000 + +struct map_info flash_map = { + .name = "flash device", + .size = FLASH_SIZE, + .bankwidth = 1, +}; + +struct mtd_partition flash_parts[] = { + { + .name = "Bootloader", + .offset = FLASH_PARTITION0_ADDR, + .size = FLASH_PARTITION0_SIZE + }, +}; + +#define PARTITION_COUNT ARRAY_SIZE(flash_parts) + +static struct mtd_info *mymtd; + +int __init init_flash(void) +{ + printk(KERN_NOTICE "Flash flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + + flash_map.phys = FLASH_PHYS_ADDR; + flash_map.virt = ioremap(FLASH_PHYS_ADDR, + FLASH_SIZE); + + if (!flash_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&flash_map); + + mymtd = do_map_probe("jedec_probe", &flash_map); + if (mymtd) { + mtd_device_register(mymtd, flash_parts, PARTITION_COUNT); + printk(KERN_NOTICE "pmon flash device initialized\n"); + return 0; + } + + iounmap((void *)flash_map.virt); + return -ENXIO; +} + +static void __exit cleanup_flash(void) +{ + if (mymtd) { + mtd_device_unregister(mymtd); + map_destroy(mymtd); + } + if (flash_map.virt) { + iounmap((void *)flash_map.virt); + flash_map.virt = 0; + } +} + +module_init(init_flash); +module_exit(cleanup_flash); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yanhua"); +MODULE_DESCRIPTION("MTD map driver for pmon programming module"); From 130d3c218920955a6ffe16291a60bb4a455ec272 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 5 Dec 2024 16:19:39 +0800 Subject: [PATCH 480/521] AOSCOS: platform: pmon_flash: mark init_flash() function as static Suppress a missing prototypes warning during build. Fixes: "AOSCOS: MIPS: Loongson: Add PMON read/write in OS support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/mips/pmon_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/pmon_flash.c b/drivers/platform/mips/pmon_flash.c index 8fd5f4bf8b1bb3..6c5a3c4342b294 100644 --- a/drivers/platform/mips/pmon_flash.c +++ b/drivers/platform/mips/pmon_flash.c @@ -53,7 +53,7 @@ struct mtd_partition flash_parts[] = { static struct mtd_info *mymtd; -int __init init_flash(void) +static int __init init_flash(void) { printk(KERN_NOTICE "Flash flash device: %x at %x\n", FLASH_SIZE, FLASH_PHYS_ADDR); From 81b8b92686cdb112874f841bea409c998e855c8d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 00:29:07 +0800 Subject: [PATCH 481/521] AOSCOS: MIPS: math-emu: replace CPU_LOONGSON3 conditions with CPU_LOONGSON64 Per commit 30ad29bb4888 ("MIPS: Loongson: Naming style cleanup and rework"), rename the CPU_LOONGSON3 definition as CPU_LOONGSON64. Fixes: "FROMLIST: MIPS: math-emu: Add madd/msub/nmadd/nmsub emulation for Loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/math-emu/cp1emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index cc0360e8dd62ca..fc1d0c83b554aa 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1451,7 +1451,7 @@ static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); } -#ifndef CONFIG_CPU_LOONGSON3 +#ifndef CONFIG_CPU_LOONGSON64 DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); @@ -1515,7 +1515,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; -#ifdef CONFIG_CPU_LOONGSON3 +#ifdef CONFIG_CPU_LOONGSON64 case madd_s_op: handler = ieee754sp_madd; goto scoptop; @@ -1638,7 +1638,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; -#ifdef CONFIG_CPU_LOONGSON3 +#ifdef CONFIG_CPU_LOONGSON64 case madd_d_op: handler = ieee754dp_madd; goto dcoptop; From a71edefb13487f797ff131c42df53e069cc09d51 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 482/521] AOSCOS: Optimize clear_page Signed-off-by: chenj Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/include/asm/uasm.h | 5 +++++ arch/mips/include/uapi/asm/inst.h | 7 +++++++ arch/mips/mm/page.c | 9 +++++++++ arch/mips/mm/uasm-mips.c | 24 ++++++++++++++++++++++++ arch/mips/mm/uasm.c | 12 +++++++++++- 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h index b43bfd44525213..2573d559320e63 100644 --- a/arch/mips/include/asm/uasm.h +++ b/arch/mips/include/asm/uasm.h @@ -42,6 +42,10 @@ void uasm_i##op(u32 **buf, unsigned int a, signed int b, unsigned int c) #define Ip_s3s1s2(op) \ void uasm_i##op(u32 **buf, int a, int b, int c) +#define Ip_u4u2u1s3(op) \ +void uasm_i##op(u32 **buf, unsigned int a, unsigned int b, signed int c, \ + unsigned int d) + #define Ip_u2u1s3(op) \ void uasm_i##op(u32 **buf, unsigned int a, unsigned int b, signed int c) @@ -159,6 +163,7 @@ Ip_u2s3u1(_sd); Ip_u3u1u2(_seleqz); Ip_u3u1u2(_selnez); Ip_u2s3u1(_sh); +Ip_u4u2u1s3(_gssq); Ip_u2u1u3(_sll); Ip_u3u2u1(_sllv); Ip_s3s1s2(_slt); diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h index c29dbc8c1d491e..6d41c8bbbdec2c 100644 --- a/arch/mips/include/uapi/asm/inst.h +++ b/arch/mips/include/uapi/asm/inst.h @@ -137,6 +137,13 @@ enum ddivu_op { ddivu_dmodu_op = 0x3, }; +/* + * func field of spec opcode. + */ +enum swc2_op { + gssq_op = 0x20, +}; + /* * rt field of bcond opcodes. */ diff --git a/arch/mips/mm/page.c b/arch/mips/mm/page.c index 1df237bd4a72bd..21c9d35ec25e86 100644 --- a/arch/mips/mm/page.c +++ b/arch/mips/mm/page.c @@ -206,6 +206,11 @@ static void set_prefetch_parameters(void) else if (cpu_has_cache_cdex_p) cache_line_size = cpu_dcache_line_size(); } + +#ifdef CONFIG_CPU_LOONGSON3 + clear_word_size = 16; +#endif + /* * Too much unrolling will overflow the available space in * clear_space_array / copy_page_array. @@ -220,11 +225,15 @@ static void set_prefetch_parameters(void) static void build_clear_store(u32 **buf, int off) { +#ifdef CONFIG_CPU_LOONGSON3 + uasm_i_gssq(buf, ZERO, ZERO, off, A0); +#else if (cpu_has_64bit_gp_regs || cpu_has_64bit_zero_reg) { uasm_i_sd(buf, GPR_ZERO, off, GPR_A0); } else { uasm_i_sw(buf, GPR_ZERO, off, GPR_A0); } +#endif } static inline void build_clear_pref(u32 **buf, int off) diff --git a/arch/mips/mm/uasm-mips.c b/arch/mips/mm/uasm-mips.c index e15c6700cd0884..7e44ea365795b1 100644 --- a/arch/mips/mm/uasm-mips.c +++ b/arch/mips/mm/uasm-mips.c @@ -27,6 +27,10 @@ #define RT_SH 16 #define SCIMM_MASK 0xfffff #define SCIMM_SH 6 +#define RZ_MASK 0x1f +#define RZ_SH 0 +#define RC_MASK 0x1ff +#define RC_SH 6 /* This macro sets the non-variable bits of an instruction. */ #define M(a, b, c, d, e, f) \ @@ -203,6 +207,7 @@ static const struct insn insn_table[insn_invalid] = { [insn_xor] = {M(spec_op, 0, 0, 0, 0, xor_op), RS | RT | RD}, [insn_xori] = {M(xori_op, 0, 0, 0, 0, 0), RS | RT | UIMM}, [insn_yield] = {M(spec3_op, 0, 0, 0, 0, yield_op), RS | RD}, + [insn_gssq] = {M(swc2_op, 0, 0, 0, 0, gssq_op), RT | RS | RZ | RC}, }; #undef M @@ -225,6 +230,21 @@ static inline u32 build_jimm(u32 arg) return (arg >> 2) & JIMM_MASK; } +static inline u32 build_rz(u32 arg) +{ + WARN(arg & ~RZ_MASK, KERN_WARNING "Micro-assembler field overflow\n"); + + return (arg & RZ_MASK) << RZ_SH; +} + +static inline u32 build_rc(s32 arg) +{ + WARN((arg >> 4) > 0xff || + (arg >> 4) < -0x100, KERN_WARNING "Micro-assembler field overflow\n"); + + return ((arg >> 4) & RC_MASK) << RC_SH; +} + /* * The order of opcode arguments is implicitly left to right, * starting with RS and ending with FUNC or IMM. @@ -252,6 +272,10 @@ static void build_insn(u32 **buf, enum opcode opc, ...) op |= build_rd(va_arg(ap, u32)); if (ip->fields & RE) op |= build_re(va_arg(ap, u32)); + if (ip->fields & RZ) + op |= build_rz(va_arg(ap, u32)); + if (ip->fields & RC) + op |= build_rc(va_arg(ap, s32)); if (ip->fields & SIMM) op |= build_simm(va_arg(ap, s32)); if (ip->fields & UIMM) diff --git a/arch/mips/mm/uasm.c b/arch/mips/mm/uasm.c index 125140979d62c5..2a9ca7de437603 100644 --- a/arch/mips/mm/uasm.c +++ b/arch/mips/mm/uasm.c @@ -26,6 +26,8 @@ enum fields { SET = 0x200, SCIMM = 0x400, SIMM9 = 0x800, + RZ = 0x1000, + RC = 0x2000 }; #define OP_MASK 0x3f @@ -65,7 +67,7 @@ enum opcode { insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra, insn_srav, insn_srl, insn_srlv, insn_subu, insn_sw, insn_sync, insn_syscall, insn_tlbp, insn_tlbr, insn_tlbwi, insn_tlbwr, insn_wait, - insn_wsbh, insn_xor, insn_xori, insn_yield, + insn_wsbh, insn_xor, insn_xori, insn_yield, insn_gssq, insn_invalid /* insn_invalid must be last */ }; @@ -198,6 +200,13 @@ Ip_u2s3u1(op) \ } \ UASM_EXPORT_SYMBOL(uasm_i##op); +#define I_u4u2u1s3(op) \ +Ip_u4u2u1s3(op) \ +{ \ + build_insn(buf, insn##op, d, b, a, c); \ +} \ +UASM_EXPORT_SYMBOL(uasm_i##op); + #define I_u2u1s3(op) \ Ip_u2u1s3(op) \ { \ @@ -356,6 +365,7 @@ I_u2s3u1(_sd) I_u3u1u2(_seleqz) I_u3u1u2(_selnez) I_u2s3u1(_sh) +I_u4u2u1s3(_gssq) I_u2u1u3(_sll) I_u3u2u1(_sllv) I_s3s1s2(_slt) From 8daa70429f711e2913130fe6a627714402091585 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 483/521] AOSCOS: memset optimization for loongson-3 [Mingcong Bai: Resolved a minor conflict in arch/mips/loongson64/Makefile.] Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/Makefile | 1 + arch/mips/loongson64/loongson3-memset.S | 206 ++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 arch/mips/loongson64/loongson3-memset.S diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index ba083beb6ca818..ed7e550913fe8e 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ constant_timer.o \ + loongson3-memset.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S new file mode 100644 index 00000000000000..227b2d67082e30 --- /dev/null +++ b/arch/mips/loongson64/loongson3-memset.S @@ -0,0 +1,206 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1998, 1999, 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2007 by Maciej W. Rozycki + * Copyright (C) 2011, 2012 MIPS Technologies, Inc. + */ +#include +#include +#include +#include + +#define LONG_S_L sdl +#define LONG_S_R sdr + +#define STORSIZE 16 +#define STORMASK 15 + +#define EX(insn,reg,addr,handler) \ +9: insn reg, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#define EX_GSSQ(reg, addr, handler) \ + .set push; \ + .set arch=loongson3a; \ +9: gssq reg, reg, addr; \ + .set pop; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + + .macro f_fill128 dst, offset, val, fixup + EX_GSSQ(\val, (\offset + 0 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 1 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 2 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 3 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 4 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 5 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 6 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 7 * STORSIZE)(\dst), \fixup) + .endm + +/* + * memset(void *s, int c, size_t n) + * + * a0: start of area to clear + * a1: char to fill with + * a2: size of area to clear + */ + .set noreorder + .align 5 +LEAF(memset) +EXPORT_SYMBOL(memset) + beqz a1, 1f + move v0, a0 /* result */ + + andi a1, 0xff /* spread fillword */ + LONG_SLL t1, a1, 8 + or a1, t1 + LONG_SLL t1, a1, 16 + or a1, t1 + LONG_SLL t1, a1, 32 + or a1, t1 +1: + +FEXPORT(__bzero) +EXPORT_SYMBOL(__bzero) + sltiu t0, a2, STORSIZE /* very small region? */ + bnez t0, .Lsmall_memset + andi t0, a0, STORMASK /* aligned? */ + + .set noat + li AT, STORSIZE + beqz t0, 1f + PTR_SUBU t0, AT /* alignment in bytes */ + .set at + + EX(LONG_S_R, a1, (a0), .Lfirst_fixup) /* make word/dword 8B aligned */ + .set push + .set arch=mips64r2 + PTR_ADDIU t1, a0, 8 + dins t1, zero, 0, 3 + .set pop + EX(LONG_S, a1, (t1), .Lsecond_fixup) /* May double copy 8B */ + + PTR_SUBU a0, t0 /* long align ptr */ + PTR_ADDU a2, t0 /* correct size */ + +1: ori t1, a2, 0x7f /* # of full blocks */ + xori t1, 0x7f + beqz t1, .Lmemset_partial /* no block to fill */ + andi t0, a2, 0x80-STORSIZE + + PTR_ADDU t1, a0 /* end address */ + .set reorder +1: PTR_ADDIU a0, 128 + f_fill128 a0, -128, a1, .Lfwd_fixup + bne t1, a0, 1b + .set noreorder + +.Lmemset_partial: + PTR_LA t1, 2f /* where to start */ + .set noat + LONG_SRL AT, t0, 2 + PTR_SUBU t1, AT + .set at + jr t1 + PTR_ADDU a0, t0 /* dest ptr */ + + .set push + .set noreorder + .set nomacro + f_fill128 a0, -128, a1, .Lpartial_fixup /* ... but first do 16Bs ... */ +2: .set pop + andi a2, STORMASK /* At most 15B to go */ + + beqz a2, 1f + PTR_ADDU a0, a2 /* What's left */ + .set push + .set arch=mips64r2 + PTR_ADDI t1, a0, -8 + dins t1, zero, 0, 3 + .set pop + EX(LONG_S, a1, (t1), .Lnotlast_fixup) /* May double copy 8B */ + EX(LONG_S_L, a1, -1(a0), .Llast_fixup) +1: jr ra + move a2, zero + +.Lsmall_memset: + andi t1, a2, 7 + beq t1, a2, 1f + LONG_SLL t1, 2 + + EX(LONG_S_R, a1, (a0), .Lfirst_fixup) + EX(LONG_S_L, a1, 7(a0), .Lsmall_memset_fixup) + +1: PTR_LA t0, 2f + PTR_SUBU t1, t0, t1 + jr t1 + PTR_ADDU a0, a2 + + EX(sb, a1, -7(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -6(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -5(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -4(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -3(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -2(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -1(a0), .Lsmall_memset_partial_fixup) + +2: jr ra /* done */ + move a2, zero + END(memset) + +.Lsmall_memset_fixup: + PTR_ADDIU t0, a0, 8 + .set push + .set arch=mips64r2 + dins t0, zero, 0, 3 + .set pop + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t0 + +.Lsmall_memset_partial_fixup: + PTR_L t0, TI_TASK($28) + LONG_L t0, THREAD_BUADDR(t0) + jr ra + LONG_SUBU a2, a0, t0 + +.Lfirst_fixup: + jr ra + nop + +.Lsecond_fixup: + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t1 + +.Lfwd_fixup: + PTR_L t0, TI_TASK($28) + andi a2, 0x7f + LONG_L t0, THREAD_BUADDR(t0) + LONG_ADDU a2, t1 + jr ra + LONG_SUBU a2, t0 + +.Lpartial_fixup: + PTR_L t0, TI_TASK($28) + andi a2, STORMASK + LONG_L t0, THREAD_BUADDR(t0) + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t0 + +.Llast_fixup: + jr ra + andi a2, 0x7 + +.Lnotlast_fixup: + jr ra + PTR_SUBU a2, a0, t1 From 286a75ab9805e5d95a498b58758d7f055734c7e1 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 01:27:08 +0800 Subject: [PATCH 484/521] AOSCOS: MIPS: loongson3-memset: replace with Per commit 9259e15b3f27 ("mips: replace #include with for . Follow the commit and revise loongson3-memset.S accordingly. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memset.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S index 227b2d67082e30..0a35c013f6df49 100644 --- a/arch/mips/loongson64/loongson3-memset.S +++ b/arch/mips/loongson64/loongson3-memset.S @@ -8,9 +8,9 @@ * Copyright (C) 2007 by Maciej W. Rozycki * Copyright (C) 2011, 2012 MIPS Technologies, Inc. */ +#include #include #include -#include #include #define LONG_S_L sdl From 86f2f0d1ae2bf764917e3422ae025f9ea0f8fc62 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 13:30:55 +0800 Subject: [PATCH 485/521] AOSCOS: MIPS: loongson3-memset: use PTR_WD to fix build Per commit fa62f39dc7e2 ("MIPS: Fix build error due to PTR used in more places"), `PTR' was renamed to `PTR_WD' to avoid definition conflicts. Follow this commit to fix build. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memset.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S index 0a35c013f6df49..55c3b8d7bc458e 100644 --- a/arch/mips/loongson64/loongson3-memset.S +++ b/arch/mips/loongson64/loongson3-memset.S @@ -22,7 +22,7 @@ #define EX(insn,reg,addr,handler) \ 9: insn reg, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #define EX_GSSQ(reg, addr, handler) \ @@ -31,7 +31,7 @@ 9: gssq reg, reg, addr; \ .set pop; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous .macro f_fill128 dst, offset, val, fixup From 1ef9aa161c51dd730a5373bb51bcd40937fb4753 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 13:33:50 +0800 Subject: [PATCH 486/521] AOSCOS: MIPS: lib: exclude generic memset.o if CPU_LOONGSON64 is set In "AOSCOS: memset optimization for loongson-3", a Loongson-3-specific memset implementation was introduced, with its own set of definitions for `bzero' and `memset' routines. This resulted in multiple definitions for the aforementioned assembly routines, causing vmlinux to fail to link. Exclude generic memset.o if `CPU_LOONGSON64' is set to avoid this error. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/lib/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 5d5b993cbc2bff..b5532bab9337bd 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -10,6 +10,7 @@ lib-y += bitops.o csum_partial.o delay.o memcpy.o memset.o \ obj-y += iomap_copy.o obj-$(CONFIG_PCI) += iomap-pci.o lib-$(CONFIG_GENERIC_CSUM) := $(filter-out csum_partial.o, $(lib-y)) +lib-$(CONFIG_CPU_LOONGSON64) := $(filter-out memset.o, $(lib-y)) obj-$(CONFIG_CPU_GENERIC_DUMP_TLB) += dump_tlb.o obj-$(CONFIG_CPU_R3000) += r3k_dump_tlb.o From 5e0349eb272b33dfce12656bdb9dd2f69e3b68f7 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 487/521] AOSCOS: memcpy optimization for loongson-3 [Mingcong Bai: Resolved a minor merge conflict in arch/mips/loongson64/Makefile.] Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/Makefile | 2 +- arch/mips/loongson64/loongson3-memcpy.S | 506 ++++++++++++++++++++++++ 2 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 arch/mips/loongson64/loongson3-memcpy.S diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index ed7e550913fe8e..438828f554a259 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ constant_timer.o \ - loongson3-memset.o + loongson3-memset.o loongson3-memcpy.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S new file mode 100644 index 00000000000000..28b785363351c8 --- /dev/null +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -0,0 +1,506 @@ +/* + ============================================================================ + Name : memcpy.S + Author : Heiher + Chen Jie + Version : 20140307 + Copyright : GPLv2 + Description : The memcpy for Loongson 3. + ============================================================================ + */ + +#define LINUX_KERNEL +#ifndef LINUX_KERNEL +#include +#include + +#define EXC(inst_reg,addr,handler) \ + inst_reg, addr; +#define EXCQ(inst,reg1,reg2,addr,handler) \ + inst reg1, reg2, addr; + +#if _MIPS_SIM == _ABI64 +#define CONFIG_64BIT +#else +#define CONFIG_32BIT +#endif + +#define FEXPORT(symbol) + +#else /* LINUX_KERNEL */ + +#include +#include +#include +#include + +#define EXC(inst_reg,addr,handler) \ +9: inst_reg, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#define EXCQ(inst,reg1,reg2,addr,handler) \ +9: inst reg1, reg2, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#endif + +#define dst a0 +#define src a1 +#define len a2 +#define rem t8 + +/* + * 64bit ABI vs 32bit ABI + */ +#ifdef CONFIG_64BIT +#define ADDU daddu +#define ADDI daddi +#define SUBU dsubu +#define SLL dsll +#define SRL dsrl +#define PTR_LA dla + +#define LOAD ld + +/* + * As we are sharing code base with the mips32 tree (which use the o32 ABI + * register definitions). We need to redefine the register definitions from + * the n64 ABI register naming to the o32 ABI register naming. + */ +#undef t0 +#undef t1 +#undef t2 +#undef t3 +#define t0 $8 +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 + +#else + +#define ADDU addu +#define ADDI addi +#define SUBU subu +#define SLL sll +#define SRL srl +#define PTR_LA la + +#define LOAD lw +#endif /* CONFIG_64BIT */ + +#define LDFIRST ldr +#define LDREST ldl + +#define SDFIRST sdr +#define SDREST sdl + +#define LWFIRST lwr +#define LWREST lwl + +#define SWFIRST swr +#define SWREST swl + +/* void * memcpy (void *s1, const void *s2, size_t n); */ + .text + .align 5 + .set noreorder + .set noat + .set arch=loongson3a + +LEAF(memcpy) /* a0=dst a1=src a2=len */ +EXPORT_SYMBOL(memcpy) + move v0, dst +.L__memcpy: +FEXPORT(__copy_user) +EXPORT_SYMBOL(__copy_user) + /* if less then 0x28 bytes */ + sltu t2, a2, 0x28 + andi t0, dst, 0xf + bnez t2, .L_memcpy_less + andi t1, src, 0xf + + beqz t0, 1f + ADDI rem, t0, -0x10 + + /* upgrade */ +EXC( LDFIRST t3, 0(src), .Ll_exc) + sltu t4, t0, 0x8 +EXC( LDREST t3, 7(src), .Ll_exc_copy) + SUBU src, rem +EXC( SDFIRST t3, 0(dst), .Ls_exc) + SUBU dst, rem + beqz t4, 1f + ADDU len, rem +EXC( LDFIRST t3, -8(src), .Ll_exc_a8) +EXC( LDREST t3, -1(src), .Ll_exc_copy_a8) +EXC( sd t3, -8(dst), .Ls_exc_p8) + +1: andi t7, src, 0x7 + beq t0, t1, .L_memcpy_16_16 + nop + bnez t7, .L_memcpy_16_4_2_1 + +.L_memcpy_16_8: + SRL t0, len, 6 # 64B per iteration + beqz t0, 2f + and rem, len, 0x3f + .align 4 +1: +EXC( ld t4, (16 * 0)(src), .Ll_exc) +EXC( ld t7, (8 + 16 * 0)(src), .Ll_exc_copy) +EXC( ld t2, (16 * 1)(src), .Ll_exc_copy) +EXC( ld t3, (8 + 16 * 1)(src), .Ll_exc_copy) +EXC( ld t0, (16 * 2)(src), .Ll_exc_copy) +EXC( ld t1, (8 + 16 * 2)(src), .Ll_exc_copy) + ADDI len, -16 * 4 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p64) +EXC( ld t4, (16 * 3)(src), .Ll_exc_copy) +EXC( ld t7, (8 + 16 * 3)(src), .Ll_exc_copy) + ADDU src, 16 * 4 + ADDU dst, 16 * 4 +EXCQ( gssq, t3, t2, (-16 * 3)(dst), .Ls_exc_p48) +EXCQ( gssq, t1, t0, (-16 * 2)(dst), .Ls_exc_p32) +EXCQ( gssq, t7, t4, (-16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + nop + beqz len, .Ldone +2: sltu t0, len, 32 + bnez t0, 3f + and rem, len, 0xf +EXC( ld t2, (16 * 0)(src), .Ll_exc) +EXC( ld t3, (8 + 16 * 0)(src), .Ll_exc_copy) +EXC( ld t0, (16 * 1)(src), .Ll_exc_copy) +EXC( ld t1, (8 + 16 * 1)(src), .Ll_exc_copy) + ADDI len, -16 * 2 + ADDU src, 16 * 2 +EXCQ( gssq, t3, t2, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t1, t0, (16 * 1)(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 32 +3: /* copy less than 32B */ + beq rem, len, .L_memcpy_1_15B_8B_aligned + nop +EXC( ld t2, 0(src), .Ll_exc) +EXC( ld t3, 8(src), .Ll_exc_copy) + ADDI len, -16 + ADDU src, 16 +EXCQ( gssq, t3, t2, 0(dst), .Ls_exc_p16) + bnez len, .L_memcpy_1_15B_8B_aligned + ADDU dst, 16 + + jr ra + nop + +.L_memcpy_16_16: + SRL t0, len, 6 # 64B per iteration + beqz t0, 2f + and rem, len, 0x3f + .align 4 +1: +EXCQ( gslq, t7, t4, (16 * 0)(src), .Ll_exc) +EXCQ( gslq, t3, t2, (16 * 1)(src), .Ll_exc_copy) +EXCQ( gslq, t1, t0, (16 * 2)(src), .Ll_exc_copy) + ADDI len, -16 * 4 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p64) +EXCQ( gslq, t7, t4, (16 * 3)(src), .Ll_exc_copy) + ADDU src, 16 * 4 + ADDU dst, 16 * 4 +EXCQ( gssq, t3, t2, (-16 * 3)(dst), .Ls_exc_p48) +EXCQ( gssq, t1, t0, (-16 * 2)(dst), .Ls_exc_p32) +EXCQ( gssq, t7, t4, (-16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + nop + beqz len, .Ldone +2: sltu t0, len, 32 + bnez t0, 3f + and rem, len, 0xf +EXCQ( gslq, t3, t2, (16 * 0)(src), .Ll_exc) +EXCQ( gslq, t1, t0, (16 * 1)(src), .Ll_exc_copy) + ADDI len, -16 * 2 + ADDU src, 32 +EXCQ( gssq, t3, t2, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t1, t0, (16 * 1)(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 32 +3: /* copy less than 32B */ + beq rem, len, .L_memcpy_1_15B_8B_aligned + nop +EXCQ( gslq, t3, t2, 0(src), .Ll_exc) + ADDI len, -16 + ADDU src, 16 +EXCQ( gssq, t3, t2, 0(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 16 +/* + * copy 1 - 15B, src & dst are 8B aligned + */ +.L_memcpy_1_15B_8B_aligned: + sltu t0, len, 0x9 + bnez t0, 1f + nop +EXC( ld t1, (src), .Ll_exc) +EXC( sd t1, (dst), .Ls_exc) +1: ADDU src, len + ADDU dst, len +EXC( LDREST t1, -1(src), .Ll_exc_copy_len) +EXC( SDREST t1, -1(dst), .Ls_exc) +.Ldone: + jr ra + move len, zero + +.L_memcpy_16_4_2_1: + SRL t0, len, 5 # 32B per iteration + beqz t0, 2f + and rem, len, 0x1f +1: +EXC( LDFIRST t4, 0(src), .Ll_exc) +EXC( LDFIRST t7, 8(src), .Ll_exc_copy) + ADDI len, -16 * 2 +EXC( LDREST t4, 7(src), .Ll_exc_copy) +EXC( LDREST t7, 15(src), .Ll_exc_copy) +EXC( LDFIRST t2, 16(src), .Ll_exc_copy) +EXC( LDFIRST t3, 24(src), .Ll_exc_copy) +EXC( LDREST t2, 23(src), .Ll_exc_copy) +EXC( LDREST t3, 31(src), .Ll_exc_copy) + ADDU src, 16 * 2 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t3, t2, (16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + ADDU dst, 16 * 2 + beqz len, .Ldone +2: and rem, len, 0xf + beq rem, len, .L_memcpy_less + nop +EXC( LDFIRST t0, 0(src), .Ll_exc) +EXC( LDFIRST t1, 8(src), .Ll_exc_copy) + ADDI len, -16 +EXC( LDREST t0, 7(src), .Ll_exc_copy) +EXC( LDREST t1, 15(src), .Ll_exc_copy) + ADDU src, 16 +EXCQ( gssq, t1, t0, 0(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 16 + +.L_memcpy_less: + andi t0, len, 0x7 + beq t0, len, 2f + andi t4, len, 0x3 + + .set reorder + SUBU t1, len, t0 + ADDU dst, t1 + ADDU src, t1 + .set at=t2 + PTR_LA t3, 1f + .set noat + SLL t2, t1, 0x1 /* 4 * 4B instructions move 8B data*/ + SUBU t3, t2 + jr t3 + .set noreorder + +EXC( LDFIRST t2, (-8 * 6)(src), .Ll_exc_a48) +EXC( LDREST t2, (-8 * 6 + 7)(src), .Ll_exc_copy_a48) +EXC( SDFIRST t2, (-8 * 6)(dst), .Ls_exc) +EXC( SDREST t2, (-8 * 6 + 7)(dst), .Ls_exc_a8) + +EXC( LDFIRST t3, (-8 * 5)(src), .Ll_exc_a40) +EXC( LDREST t3, (-8 * 5 + 7)(src), .Ll_exc_copy_a40) +EXC( SDFIRST t3, (-8 * 5)(dst), .Ls_exc_a8) +EXC( SDREST t3, (-8 * 5 + 7)(dst), .Ls_exc_a16) + +EXC( LDFIRST t1, (-8 * 4)(src), .Ll_exc_a32) +EXC( LDREST t1, (-8 * 4 + 7)(src), .Ll_exc_copy_a32) +EXC( SDFIRST t1, (-8 * 4)(dst), .Ls_exc_a16) +EXC( SDREST t1, (-8 * 4 + 7)(dst), .Ls_exc_a24) + +EXC( LDFIRST t2, (-8 * 3)(src), .Ll_exc_a24) +EXC( LDREST t2, (-8 * 3 + 7)(src), .Ll_exc_copy_a24) +EXC( SDFIRST t2, (-8 * 3)(dst), .Ls_exc_a24) +EXC( SDREST t2, (-8 * 3 + 7)(dst), .Ls_exc_a32) + +EXC( LDFIRST t3, (-8 * 2)(src), .Ll_exc_a16) +EXC( LDREST t3, (-8 * 2 + 7)(src), .Ll_exc_copy_a16) +EXC( SDFIRST t3, (-8 * 2)(dst), .Ls_exc_a32) +EXC( SDREST t3, (-8 * 2 + 7)(dst), .Ls_exc_a40) + +EXC( LDFIRST t1, (-8 * 1)(src), .Ll_exc_a8) +EXC( LDREST t1, (-8 * 1 + 7)(src), .Ll_exc_copy_a8) +EXC( SDFIRST t1, (-8 * 1)(dst), .Ls_exc_a40) +EXC( SDREST t1, (-8 * 1 + 7)(dst), .Ls_exc_a48) +1: beqz t0, .Ldone + ADDU src, t0 + ADDU dst, t0 +EXC( LDFIRST t2, -8(src), .Ll_exc_a8) +EXC( LDREST t2, -1(src), .Ll_exc_copy_a8) +EXC( SDFIRST t2, -8(dst), .Ls_exc) +EXC( SDREST t2, -1(dst), .Ls_exc) + jr ra + move len, zero + +2: + beq t4, len, 3f + nop +EXC( LWFIRST t2, (src), .Ll_exc) +EXC( LWREST t2, 3(src), .Ll_exc_copy) + ADDU src, len +EXC( SWFIRST t2, (dst), .Ls_exc) +EXC( SWREST t2, 3(dst), .Ls_exc) + beqz t4, .Ldone + ADDU dst, len + +EXC( LWFIRST t1, -4(src), .Ll_exc_a4) +EXC( LWREST t1, -1(src), .Ll_exc_copy_a4) +EXC( SWFIRST t1, -4(dst), .Ls_exc) +EXC( SWREST t1, -1(dst), .Ls_exc) + jr ra + move len, zero + +3: + beqz len, .Ldone + ADDU t0, src, len +1: +EXC( lb t2, (src), .Ll_exc) + ADDU src, 1 +EXC( sb t2, (dst), .Ls_exc_p1) + bne t0, src, 1b + ADDU dst, 1 + + jr ra + move len, zero + + END(memcpy) + +#ifdef LINUX_KERNEL + +#define LEXC_a(n) \ +.Ll_exc_copy_a ## n: \ + ADDI src, -n; \ + b .Ll_exc_copy; \ + ADDI dst, -n; \ +.Ll_exc_a ## n: \ + ADDI src, -n; \ + b .Ll_exc; \ + ADDI dst, -n; + +.Ll_exc_copy_len: + SUBU src, len + b .Ll_exc_copy + SUBU dst, len + +LEXC_a(4) +LEXC_a(8) +LEXC_a(16) +LEXC_a(24) +LEXC_a(32) +LEXC_a(40) +LEXC_a(48) + +.Ll_exc_copy: + /* + * Copy bytes from src until faulting load address (or until a + * lb faults) + * + * When reached by a faulting LDFIRST/LDREST, THREAD_BUADDR($28) + * may be more than a byte beyond the last address. + * Hence, the lb below may get an exception. + * + * Assumes src < THREAD_BUADDR($28) + */ + LOAD t0, TI_TASK($28) + nop + LOAD t0, THREAD_BUADDR(t0) +1: +EXC( lb t1, 0(src), .Ll_exc) + ADDU src, 1 + sb t1, 0(dst) # can't fault -- we're copy_from_user + bne src, t0, 1b + ADDU dst, 1 +.Ll_exc: + LOAD t0, TI_TASK($28) + nop + LOAD t0, THREAD_BUADDR(t0) # t0 is just past last good address + nop + SUBU len, AT, t0 # len number of uncopied bytes + jr ra + nop + +#define SEXC_p(n) \ +.Ls_exc_p ## n: \ + jr ra; \ + ADDU len, n; + +#define SEXC_a(n) \ +.Ls_exc_a ## n: \ + jr ra; \ + ADDI len, -n; + +SEXC_p(1) +SEXC_p(8) +SEXC_p(16) +SEXC_p(32) +SEXC_p(48) +SEXC_p(64) +SEXC_a(48) +SEXC_a(40) +SEXC_a(32) +SEXC_a(24) +SEXC_a(16) +SEXC_a(8) + +.Ls_exc: + jr ra + nop + + .align 5 +LEAF(memmove) +EXPORT_SYMBOL(memmove) + ADDU t0, a0, a2 + ADDU t1, a1, a2 + sltu t0, a1, t0 # dst + len <= src -> memcpy + sltu t1, a0, t1 # dst >= src + len -> memcpy + and t0, t1 + beqz t0, .L__memcpy + move v0, a0 /* return value */ + beqz a2, .Lr_out + END(memmove) + + /* fall through to __rmemcpy */ +LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ + sltu t0, a1, a0 + beqz t0, .Lr_end_bytes_up # src >= dst + nop + ADDU a0, a2 # dst = dst + len + ADDU a1, a2 # src = src + len + +.Lr_end_bytes: + lb t0, -1(a1) + ADDI a2, -0x1 + sb t0, -1(a0) + ADDI a1, -0x1 + bnez a2, .Lr_end_bytes + ADDI a0, -0x1 + +.Lr_out: + jr ra + move a2, zero + +.Lr_end_bytes_up: + lb t0, (a1) + ADDI a2, -0x1 + sb t0, (a0) + ADDU a1, 0x1 + bnez a2, .Lr_end_bytes_up + ADDU a0, 0x1 + + jr ra + move a2, zero + END(__rmemcpy) +#endif From 314fb5c9cdcf81df12b529924bf01b1121752c2c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:03:01 +0800 Subject: [PATCH 488/521] AOSCOS: MIPS: loongson3-memcpy: use PTR_WD to fix build Per commit fa62f39dc7e2 ("MIPS: Fix build error due to PTR used in more places"), `PTR' was renamed to `PTR_WD' to avoid definition conflicts. Follow this commit to fix build. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memcpy.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 28b785363351c8..4c0b6b8474f043 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -37,13 +37,13 @@ #define EXC(inst_reg,addr,handler) \ 9: inst_reg, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #define EXCQ(inst,reg1,reg2,addr,handler) \ 9: inst reg1, reg2, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #endif From e73002d04d0a8135e0ced08e030b07609f9aa148 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:04:20 +0800 Subject: [PATCH 489/521] AOSCOS: MIPS: loongson3-memcpy: replace with Per commit 9259e15b3f27 ("mips: replace #include with for . Follow the commit and revise loongson3-memcpy.S accordingly. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memcpy.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 4c0b6b8474f043..2021e75d5dd2e6 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -29,9 +29,9 @@ #else /* LINUX_KERNEL */ +#include #include #include -#include #include #define EXC(inst_reg,addr,handler) \ From 47aae00cea86c91cbf176f5843642f0e10b0e41b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:09:34 +0800 Subject: [PATCH 490/521] AOSCOS: MIPS: mark CPU_LOONGSON64 as HAVE_PLAT_MEMCPY loongson3-memcpy is a platform-specific memcpy implementation, add HAVE_PLAT_MEMCPY under CPU_LOONGSON64. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 93fc1dba4faf2d..6aa299bd8cb319 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1386,6 +1386,7 @@ config CPU_LOONGSON64 select MIPS_FP_SUPPORT select GPIOLIB select SWIOTLB + select HAVE_PLAT_MEMCPY help The Loongson GSx64(GS264/GS464/GS464E/GS464V) series of processor cores implements the MIPS64R2 instruction set with many extensions, From f7e84e5f12849cf75fb9ea084725809f2b6a0f79 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:10:57 +0800 Subject: [PATCH 491/521] AOSCOS: MIPS: loongson3-memcpy: adapt to RAW_COPY_USER Since commit 2260ea86c0e7 ("mips: switch to RAW_COPY_USER"), it is required to export `__raw_copy_from_user' and `__raw_copy_from_user', which are then referenced by certain kernel modules. Export these symbols under `__memcpy', also remove the export for the now- unused `__copy_user' symbol. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memcpy.S | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 2021e75d5dd2e6..c5dd2e2866c3c7 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -119,8 +119,10 @@ LEAF(memcpy) /* a0=dst a1=src a2=len */ EXPORT_SYMBOL(memcpy) move v0, dst .L__memcpy: -FEXPORT(__copy_user) -EXPORT_SYMBOL(__copy_user) +FEXPORT(__raw_copy_from_user) +EXPORT_SYMBOL(__raw_copy_from_user) +FEXPORT(__raw_copy_to_user) +EXPORT_SYMBOL(__raw_copy_to_user) /* if less then 0x28 bytes */ sltu t2, a2, 0x28 andi t0, dst, 0xf From 24745e0268ec1d8c620efb01c4e020a4d25077a1 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:45:22 +0800 Subject: [PATCH 492/521] AOSCOS: spi: spi-loongson: allow building on MACH_LOONGSON32/64 LS2K/LS7A chipsets are also found on some MIPS-based Loongson hardware, allow building for 32-bit (2K) and 64-bit (3A/B) hardware. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/spi/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8782514bb89b0f..800fafb938d5e2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -592,12 +592,12 @@ config SPI_LM70_LLP config SPI_LOONGSON_CORE tristate - depends on LOONGARCH || COMPILE_TEST + depends on LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST config SPI_LOONGSON_PCI tristate "Loongson SPI Controller PCI Driver Support" select SPI_LOONGSON_CORE - depends on PCI && (LOONGARCH || COMPILE_TEST) + depends on PCI && (LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST) help This bus driver supports the Loongson SPI hardware controller in the Loongson platforms and supports to use PCI framework to @@ -608,7 +608,7 @@ config SPI_LOONGSON_PCI config SPI_LOONGSON_PLATFORM tristate "Loongson SPI Controller Platform Driver Support" select SPI_LOONGSON_CORE - depends on OF && (LOONGARCH || COMPILE_TEST) + depends on OF && (LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST) help This bus driver supports the Loongson SPI hardware controller in the Loongson platforms and supports to use DTS framework to From a198a7325f73009868dc2d5b6763a56df0dab6b0 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 10:35:39 +0800 Subject: [PATCH 493/521] AOSCOS: mvsas: Optimise performance and stability on CPU_LOONGSON64 Optimize this driver conditionally for MIPS-based Loongson 3 (CPU_LOONGSON64): - Reduce queue depth of SAS disks for MIPS-based Loongson 3. - Reduce granularity and timeout value of interrupt coalescing for MIPS- based Loongson 3. Also include an optimisation: - Use bidirectional DMA for SMP request/response. This is a partial patch from the Lemote v5.4 tree, "mvsas: Optimise performance and stability", stripping out the part about "Use cyclic searching to alloc command slot" as SCSI command tag allocation for this driver was extensively revised with commit 2acf97f199f9 ("scsi: mvsas: Use sas_task_find_rq() for tagging"). Not sure if this optimization would still be worthwhile to port over. Also, since CPU_LOONGSON64 is also defined for LoongArch processors, add additionally an ifdef condition for CONFIG_MIPS to ensure that quirk code only applied to MIPS && CPU_LOONGSON64 targets. As a side note, `sas_slave_configure()' was renamed to `sas_device_configure()' in commit a25a9c85d17f ("scsi: libata: Switch to using ->device_configure"), so I revised the invocations and function namings from the original patch accordingly (`mvs_slave_configure()' => `mvs_device_configure()'). This commit also introduces and additional argument for queue limits to the `sas_device_configure()' function, so revise accordingly. Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/scsi/mvsas/mv_64xx.c | 8 ++++++++ drivers/scsi/mvsas/mv_94xx.c | 8 ++++++++ drivers/scsi/mvsas/mv_init.c | 5 +++++ drivers/scsi/mvsas/mv_sas.c | 26 ++++++++++++++++++++------ drivers/scsi/mvsas/mv_sas.h | 1 + 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c index 1f2b61de8c6326..855d8465b30f24 100644 --- a/drivers/scsi/mvsas/mv_64xx.c +++ b/drivers/scsi/mvsas/mv_64xx.c @@ -376,10 +376,14 @@ static int mvs_64xx_init(struct mvs_info *mvi) * it will make count 0. */ tmp = 0; +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff | COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN); +#endif tmp = 0x10000 | interrupt_coalescing; mw32(MVS_INT_COAL_TMOUT, tmp); @@ -748,10 +752,14 @@ static void mvs_64xx_tune_interrupt(struct mvs_info *mvi, u32 time) mw32(MVS_INT_COAL, 0); mw32(MVS_INT_COAL_TMOUT, 0x10000); } else { +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff|COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN); +#endif tmp = 0x10000 | time; mw32(MVS_INT_COAL_TMOUT, tmp); diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index fc0b8eb682045f..1d1042ab82c8c0 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -515,10 +515,14 @@ static int mvs_94xx_init(struct mvs_info *mvi) * it will make count 0. */ tmp = 0; +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff | COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN); +#endif /* default interrupt coalescing time is 128us */ tmp = 0x10000 | interrupt_coalescing; @@ -1027,10 +1031,14 @@ static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time) mw32(MVS_INT_COAL, 0); mw32(MVS_INT_COAL_TMOUT, 0x10000); } else { +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff|COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN); +#endif tmp = 0x10000 | time; mw32(MVS_INT_COAL_TMOUT, tmp); diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 5abc17a2e26109..b73ee560939265 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -10,7 +10,11 @@ #include "mv_sas.h" +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) +int interrupt_coalescing = 0x08; +#else int interrupt_coalescing = 0x80; +#endif static struct scsi_transport_template *mvs_stt; static const struct mvs_chip_info mvs_chips[] = { @@ -32,6 +36,7 @@ static const struct attribute_group *mvst_sdev_groups[]; static const struct scsi_host_template mvs_sht = { LIBSAS_SHT_BASE + .device_configure = mvs_device_configure, .scan_finished = mvs_scan_finished, .scan_start = mvs_scan_start, .can_queue = 1, diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 359226e80eae82..d8fbbfd07a1d7a 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -255,6 +255,20 @@ static void mvs_bytes_dmaed(struct mvs_info *mvi, int i, gfp_t gfp_flags) sas_notify_port_event(sas_phy, PORTE_BYTES_DMAED, gfp_flags); } +int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim) +{ + struct domain_device *dev = sdev_to_domain_dev(sdev); + + sas_device_configure(sdev, lim); + +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + if (!dev_is_sata(dev)) + scsi_change_queue_depth(sdev, MVS_QUEUE_SIZE); +#endif + + return 0; +} + void mvs_scan_start(struct Scsi_Host *shost) { int i, j; @@ -309,13 +323,13 @@ static int mvs_task_prep_smp(struct mvs_info *mvi, * DMA-map SMP request, response buffers */ sg_req = &task->smp_task.smp_req; - elem = dma_map_sg(mvi->dev, sg_req, 1, DMA_TO_DEVICE); + elem = dma_map_sg(mvi->dev, sg_req, 1, DMA_BIDIRECTIONAL); if (!elem) return -ENOMEM; req_len = sg_dma_len(sg_req); sg_resp = &task->smp_task.smp_resp; - elem = dma_map_sg(mvi->dev, sg_resp, 1, DMA_FROM_DEVICE); + elem = dma_map_sg(mvi->dev, sg_resp, 1, DMA_BIDIRECTIONAL); if (!elem) { rc = -ENOMEM; goto err_out; @@ -389,10 +403,10 @@ static int mvs_task_prep_smp(struct mvs_info *mvi, err_out_2: dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_resp, 1, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); err_out: dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_req, 1, - DMA_TO_DEVICE); + DMA_BIDIRECTIONAL); return rc; } @@ -869,9 +883,9 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task, switch (task->task_proto) { case SAS_PROTOCOL_SMP: dma_unmap_sg(mvi->dev, &task->smp_task.smp_resp, 1, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); dma_unmap_sg(mvi->dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); + DMA_BIDIRECTIONAL); break; case SAS_PROTOCOL_SATA: diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index 09ce3f2241f240..b6f5b739d67c93 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -429,6 +429,7 @@ int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata); void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo, u32 off_hi, u64 sas_addr); +int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim); void mvs_scan_start(struct Scsi_Host *shost); int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time); int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags); From c6dcdadb99699b4a89f2c2ab1881cf582da99e3b Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 4 Feb 2025 12:31:07 +0800 Subject: [PATCH 494/521] AOSCOS: mvsas: Rename .device_configure() into .sdev_configure() Following upstream name changes; Also move the struct domain_device inside the ifdef to avoid unused variables. Fixes: 47c2e30afcec ("scsi: Rename .device_configure() into .sdev_configure()") Fixes: "AOSCOS: mvsas: Optimise performance and stability on CPU_LOONGSON64" Signed-off-by: Kexy Biscuit Signed-off-by: Xinhui Yang Signed-off-by: Mingcong Bai --- drivers/scsi/mvsas/mv_init.c | 2 +- drivers/scsi/mvsas/mv_sas.c | 8 ++++---- drivers/scsi/mvsas/mv_sas.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index b73ee560939265..642ec7f713ac90 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -36,7 +36,7 @@ static const struct attribute_group *mvst_sdev_groups[]; static const struct scsi_host_template mvs_sht = { LIBSAS_SHT_BASE - .device_configure = mvs_device_configure, + .sdev_configure = mvs_sdev_configure, .scan_finished = mvs_scan_finished, .scan_start = mvs_scan_start, .can_queue = 1, diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index d8fbbfd07a1d7a..7b4c113e8a41cf 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -255,13 +255,13 @@ static void mvs_bytes_dmaed(struct mvs_info *mvi, int i, gfp_t gfp_flags) sas_notify_port_event(sas_phy, PORTE_BYTES_DMAED, gfp_flags); } -int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim) +int mvs_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim) { - struct domain_device *dev = sdev_to_domain_dev(sdev); - - sas_device_configure(sdev, lim); + sas_sdev_configure(sdev, lim); #if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + struct domain_device *dev = sdev_to_domain_dev(sdev); + if (!dev_is_sata(dev)) scsi_change_queue_depth(sdev, MVS_QUEUE_SIZE); #endif diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index b6f5b739d67c93..6d7831bd9c0148 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -429,7 +429,7 @@ int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata); void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo, u32 off_hi, u64 sas_addr); -int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim); +int mvs_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim); void mvs_scan_start(struct Scsi_Host *shost); int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time); int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags); From cd2ff26d76b7bc754476e2b1ae621911427edbad Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:45:08 +0800 Subject: [PATCH 495/521] MARKER: AOSCOS: End of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 6aa299bd8cb319..4de8bafd90fbd7 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,7 +550,6 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC - depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From 98ac0e60fa2225940bff9b61fa5813a67518b36f Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sat, 16 Aug 2025 09:50:44 +0800 Subject: [PATCH 496/521] AOSCOS: ACPI / scan: Add pwm_lookup_entry for PWM3 on LS7A On LoongArch laptops using LG110 GPU for display, LS7A PWM3 is used for backlight brightness control. Thus we need to call pwm_add_table() so that the loonggpu driver can find the PWM for controlling the backlight. Signed-off-by: Xi Ruoyao Signed-off-by: Mingcong Bai --- drivers/acpi/Makefile | 1 + drivers/acpi/internal.h | 6 +++ drivers/acpi/loongarch/Makefile | 3 ++ drivers/acpi/loongarch/acpi-ls7a-backlight.c | 42 ++++++++++++++++++++ drivers/acpi/scan.c | 1 + 5 files changed, 53 insertions(+) create mode 100644 drivers/acpi/loongarch/Makefile create mode 100644 drivers/acpi/loongarch/acpi-ls7a-backlight.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d1b0affb844f05..2148e6a5ebccae 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -134,3 +134,4 @@ obj-$(CONFIG_ACPI_VIOT) += viot.o obj-$(CONFIG_RISCV) += riscv/ obj-$(CONFIG_X86) += x86/ +obj-$(CONFIG_LOONGARCH) += loongarch/ diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 40f875b265a942..c53b0c4777a645 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -75,6 +75,12 @@ void acpi_lpss_init(void); static inline void acpi_lpss_init(void) {} #endif +#if IS_ENABLED(CONFIG_PWM_LOONGSON) +void acpi_ls7a_pwm_init(void); +#else +static inline void acpi_ls7a_pwm_init(void) {} +#endif + void acpi_apd_init(void); acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); diff --git a/drivers/acpi/loongarch/Makefile b/drivers/acpi/loongarch/Makefile new file mode 100644 index 00000000000000..25f207f841222c --- /dev/null +++ b/drivers/acpi/loongarch/Makefile @@ -0,0 +1,3 @@ +ifdef CONFIG_PWM_LOONGSON +obj-y += acpi-ls7a-backlight.o +endif diff --git a/drivers/acpi/loongarch/acpi-ls7a-backlight.c b/drivers/acpi/loongarch/acpi-ls7a-backlight.c new file mode 100644 index 00000000000000..5363747cd3a7f0 --- /dev/null +++ b/drivers/acpi/loongarch/acpi-ls7a-backlight.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Xi Ruoyao + * + * ACPI support for devices using LG110 discrete graphics + * with LS7A PWM controlling the backlight. + */ + +#include +#include + +#include "../internal.h" + +static struct pwm_lookup ls7a_pwm_lookup[] = { + PWM_LOOKUP_WITH_MODULE("LOON0006:03", 0, NULL, "gsgpu_backlight", 0, + PWM_POLARITY_NORMAL, "pwm-loongson"), +}; + +static int acpi_ls7a_pwm_attach(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + if (acpi_dev_uid_match(adev, 3)) + pwm_add_table(ls7a_pwm_lookup, ARRAY_SIZE(ls7a_pwm_lookup)); + + return 0; +} + +static const struct acpi_device_id acpi_ls7a_pwm_ids[] = { + { "LOON0006", 0 }, + { }, +}; + +static struct acpi_scan_handler ls7a_pwm_handler = { + .ids = acpi_ls7a_pwm_ids, + .attach = acpi_ls7a_pwm_attach, +}; + + +void __init acpi_ls7a_pwm_init(void) +{ + acpi_scan_add_handler(&ls7a_pwm_handler); +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index de8df157be0bcd..d810aa6137440f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2839,6 +2839,7 @@ void __init acpi_scan_init(void) acpi_pnp_init(); acpi_power_resources_init(); acpi_init_lpit(); + acpi_ls7a_pwm_init(); acpi_scan_add_handler(&generic_device_handler); From 02987fe9952f1ab63a7dbf8b6893a9e1e6882222 Mon Sep 17 00:00:00 2001 From: Xinhui Yang Date: Wed, 10 Sep 2025 23:21:26 +0800 Subject: [PATCH 497/521] AOSCOS: scsi: dc395x: correctly discard the return value in certain reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are certain read operations performed in this code which doesn't really don't need its return value. Those read operations either clears the FIFO buffer, or clears the interruption status. However, unused read triggers compiler warnings. With CONFIG_WERROR on, these warnings get converted into errors: drivers/scsi/dc395x.c: In function ‘__dc395x_eh_bus_reset’: drivers/scsi/dc395x.c:97:49: error: value computed is not used [-Werror=unused-value] 97 | #define DC395x_read8(acb,address) (u8)(inb(acb->io_port_base + (address))) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/scsi/dc395x.c:1003:9: note: in expansion of macro ‘DC395x_read8’ 1003 | DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); | ^~~~~~~~~~~~ drivers/scsi/dc395x.c: In function ‘data_io_transfer’: drivers/scsi/dc395x.c:97:49: error: value computed is not used [-Werror=unused-value] 97 | #define DC395x_read8(acb,address) (u8)(inb(acb->io_port_base + (address))) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/scsi/dc395x.c:2032:33: note: in expansion of macro ‘DC395x_read8’ 2032 | DC395x_read8(acb, TRM_S1040_SCSI_FIFO); Create a new macro DC395x_peek8() to deliberately cast the return value to void, which tells the compiler we really don't need the return value of such read operations. Signed-off-by: Xinhui Yang Signed-off-by: Mingcong Bai --- drivers/scsi/dc395x.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index 6183ce05d8cf61..acf836312912d3 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -94,6 +94,12 @@ #define DC395x_LOCK_IO(dev,flags) spin_lock_irqsave(((struct Scsi_Host *)dev)->host_lock, flags) #define DC395x_UNLOCK_IO(dev,flags) spin_unlock_irqrestore(((struct Scsi_Host *)dev)->host_lock, flags) +/* + * read operations that may trigger side effects in the hardware, + * but the value can or should be discarded. + */ +#define DC395x_peek8(acb,address) (void)(inb(acb->io_port_base + (address))) + #define DC395x_read8(acb,address) (u8)(inb(acb->io_port_base + (address))) #define DC395x_read16(acb,address) (u16)(inw(acb->io_port_base + (address))) #define DC395x_read32(acb,address) (u32)(inl(acb->io_port_base + (address))) @@ -1000,7 +1006,7 @@ static int __dc395x_eh_bus_reset(struct scsi_cmnd *cmd) DC395x_write8(acb, TRM_S1040_DMA_CONTROL, CLRXFIFO); clear_fifo(acb, "eh_bus_reset"); /* Delete pending IRQ */ - DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); + DC395x_peek8(acb, TRM_S1040_SCSI_INTSTATUS); set_basic_config(acb); reset_dev_param(acb); @@ -2029,8 +2035,8 @@ static void data_io_transfer(struct AdapterCtlBlk *acb, DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, CFG2_WIDEFIFO); if (io_dir & DMACMD_DIR) { - DC395x_read8(acb, TRM_S1040_SCSI_FIFO); - DC395x_read8(acb, TRM_S1040_SCSI_FIFO); + DC395x_peek8(acb, TRM_S1040_SCSI_FIFO); + DC395x_peek8(acb, TRM_S1040_SCSI_FIFO); } else { /* Danger, Robinson: If you find KGs * scattered over the wide disk, the driver @@ -2044,7 +2050,7 @@ static void data_io_transfer(struct AdapterCtlBlk *acb, /* Danger, Robinson: If you find a collection of Ks on your disk * something broke :-( */ if (io_dir & DMACMD_DIR) - DC395x_read8(acb, TRM_S1040_SCSI_FIFO); + DC395x_peek8(acb, TRM_S1040_SCSI_FIFO); else DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 'K'); } @@ -2892,7 +2898,7 @@ static void set_basic_config(struct AdapterCtlBlk *acb) DMA_FIFO_HALF_HALF | DMA_ENHANCE /*| DMA_MEM_MULTI_READ */ ; DC395x_write16(acb, TRM_S1040_DMA_CONFIG, wval); /* Clear pending interrupt status */ - DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); + DC395x_peek8(acb, TRM_S1040_SCSI_INTSTATUS); /* Enable SCSI interrupt */ DC395x_write8(acb, TRM_S1040_SCSI_INTEN, 0x7F); DC395x_write8(acb, TRM_S1040_DMA_INTEN, EN_SCSIINTR | EN_DMAXFERERROR @@ -3799,7 +3805,7 @@ static void adapter_uninit_chip(struct AdapterCtlBlk *acb) reset_scsi_bus(acb); /* clear any pending interrupt state */ - DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); + DC395x_peek8(acb, TRM_S1040_SCSI_INTSTATUS); } From 1ccb753ade87e745d80dd39ee36108c06905811b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 12 Nov 2025 11:41:05 +0800 Subject: [PATCH 498/521] AOSCOS: crypto: padlock-sha: return -ENODEV on Zhaoxin KaiXian KX-6000/6000G On Zhaoxin KaiXian KX-6000/6000G series of processors, this crypto module causes a null pointer dereference. Zhaoxin submitted upstream an implementation update but does not specify a fix. Per our testing, blacklisting this module causes the null pointer dereference to go away. Block x86 Family 7 Model 59 for now. Link: https://lore.kernel.org/all/20250114121301.156359-1-TonyWWang-oc%40zhaoxin.com/ */ Signed-off-by: Mingcong Bai --- drivers/crypto/padlock-sha.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c index 9214bbfc868f5d..5104f8adfa3417 100644 --- a/drivers/crypto/padlock-sha.c +++ b/drivers/crypto/padlock-sha.c @@ -329,7 +329,17 @@ static int __init padlock_init(void) struct shash_alg *sha1; struct shash_alg *sha256; - if (!x86_match_cpu(padlock_sha_ids) || !boot_cpu_has(X86_FEATURE_PHE_EN)) + /* On Zhaoxin KaiXian KX-6000/6000G series of processors, this crypto + * module causes a null pointer dereference. + * + * Zhaoxin submitted upstream an implementation update but does not + * specify a fix. Per our testing, blacklisting this module causes the + * null pointer dereference to go away. + * + * Link: https://lore.kernel.org/all/20250114121301.156359-1-TonyWWang-oc%40zhaoxin.com/ */ + if (!x86_match_cpu(padlock_sha_ids) || + !boot_cpu_has(X86_FEATURE_PHE_EN) || + (c->x86 == 7 && c->x86_model == 59)) return -ENODEV; /* From 9d3cb04245616cabac6f06b7d940f99d0c541876 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 23 Dec 2025 20:08:10 +0800 Subject: [PATCH 499/521] AOSCOS: Revert "FROMLIST: rust: generate a fatal error if BINDGEN_TARGET is undefined" This reverts "FROMLIST: rust: generate a fatal error if BINDGEN_TARGET is undefined" as it breaks build on architectures that do not HAVE_RUST. Link: https://lore.kernel.org/all/6bd26802-e3ce-43e2-b28d-f23e13492458@aosc.io/ Signed-off-by: Mingcong Bai --- scripts/Makefile.rust | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/Makefile.rust b/scripts/Makefile.rust index b7a6b1e2a88a5e..ae51e68dcd59fd 100644 --- a/scripts/Makefile.rust +++ b/scripts/Makefile.rust @@ -10,8 +10,4 @@ BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) BINDGEN_TARGET_riscv := riscv64-linux-gnu BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) -ifeq ($(BINDGEN_TARGET),) -$(error add '--target=' option to scripts/Makefile.rust) -else export BINDGEN_TARGET -endif From e7fee20fd728f499c1acbba312a91357a5358572 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Sun, 25 Jan 2026 00:54:35 +0800 Subject: [PATCH 500/521] AOSCOS: MIPS: Loongson: Salvage the full count from the constant timer HWR $30 (Loongson64 constant timer) is 64 bits wide. However, vDSO uses `unsigned long' to read the timer count, which leads to frozen time (delta ~= 3s) when O32/N32 userspace programs request HRES time. For N32, using `u64' instead of `unsigned long' fixes the issue. The issue becomes trickier on O32. RDHWR never sign-extends the timer count before copying it into a GPR, as the kernel always sets Status.UX=1 even in O32 userspace. Aside from the frozen time, the "shadow" on the upper half also breaks subsequent logical/branch instructions relying on the GPR. Thankfully, Status.UX==1 also means that we have the full timer count available and can use 64-bit instructions to salvage it into a pair of GPR to comply with O32 ABI. Utilize (abuse?) this fact to salvage the full timer count in O32 vDSO. Fixes: "AOSCOS: MIPS: Loongson: Add constant timer support" Signed-off-by: Rong Zhang Signed-off-by: Mingcong Bai --- arch/mips/include/asm/vdso/gettimeofday.h | 25 ++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h index 8cc0beef182d18..2dba88c691edd9 100644 --- a/arch/mips/include/asm/vdso/gettimeofday.h +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -183,17 +183,40 @@ static __always_inline u64 read_gic_count(const struct vdso_time_data *data) #endif +/* + * HWR $30 (Loongson64 constant timer) is 64 bits wide. RDHWR never sign-extends + * the timer count before copying it into a GPR, as the kernel always sets + * Status.UX=1 even in O32 userspace. If it's kept as is, the "shadow" on the + * upper half breaks subsequent logical/branch instructions relying on the GPR. + * + * Thankfully, Status.UX==1 also means that we have the full timer count + * available and can use 64-bit instructions to salvage it into a pair of GPR + * to comply with O32 ABI. + */ #ifdef CONFIG_CPU_LOONGSON64 static __always_inline u64 read_const_count(void) { - unsigned long count; + u64 count; +/* N32 ABI stores 64-bit value in a single register. */ +#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 __asm__ __volatile__( " .set push\n" " .set mips32r2\n" " rdhwr %0, $30\n" " .set pop\n" : "=r" (count)); +#else /* _MIPS_SIM == _MIPS_SIM_ABI32 */ + __asm__ __volatile__( + " .set push\n" + " .set mips64r2\n" /* Enable 64-bit instructions. */ + " .set noreorder\n" + " rdhwr %0, $30\n" /* Now %0 holds the full 64-bit count. */ + " dsra %D0, %0, 32\n" /* Salvage and sign-extend the upper half. */ + " sll %0, %0, 0\n" /* Sign-extend the lower half. */ + " .set pop\n" + : "=r" (count)); +#endif return count; } From 1a20f87729e292cd28dfebd601d0582c2034f6d9 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 19 Apr 2026 02:08:51 +0800 Subject: [PATCH 501/521] AOSCOS: dt-bindings: pinctrl: mediatek: mt8188: allow gpio hogs Add gpio hogs subnode rules to the MT8188 pinctrl binding. Signed-off-by: Icenowy Zheng Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- .../devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml index e994b0c70dbfd2..1cf06e46f7bb0f 100644 --- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml @@ -67,6 +67,11 @@ properties: # PIN CONFIGURATION NODES patternProperties: + "-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + '-pins$': type: object additionalProperties: false From b7411f3ff371492bc6a5abb050b815e9460eedfe Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 19 Apr 2026 02:12:40 +0800 Subject: [PATCH 502/521] AOSCOS: arm64: dts: mediatek: mt8188-geralt: enable Wi-Fi card The mainline pcie-mediatek-gen3 driver does not have code managing downstream device power / reset. As the Wi-Fi card on ciri is a fixed device, set the related regulator to always-on and use GPIO hog to set the status of its reset pin. Signed-off-by: Icenowy Zheng Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi index 8e423504ec052c..c25780098103bf 100644 --- a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi @@ -544,6 +544,11 @@ mediatek,mic-type-2 = <2>; /* DMIC */ }; +&mt6359_vcn18_ldo_reg { + /* Used by WLAN */ + regulator-always-on; +}; + &mt6359_vcore_buck_reg { regulator-always-on; }; @@ -1145,6 +1150,12 @@ output-low; }; }; + + wlan-reset-hog { + gpio-hog; + gpios = <145 GPIO_ACTIVE_HIGH>; + output-high; + }; }; &pmic { From 6caf6f8d261a1038d41b09dbde2e4b56be832b66 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 20 Apr 2026 00:07:49 +0800 Subject: [PATCH 503/521] AOSCOS: arm64: dts: mediatek: mt8188-geralt: enable touchpad Despite the Ciri device's touchpad is on the detachable keyboard, the I2C HID device seen by Linux is emulated by ChromeOS EC and always present regardless of the presence of physical touchpad. Enable the device in the device tree. Signed-off-by: Icenowy Zheng Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- .../boot/dts/mediatek/mt8188-geralt.dtsi | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi index c25780098103bf..57cd2deb882fe3 100644 --- a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi @@ -460,6 +460,18 @@ pinctrl-0 = <&i2c4_pins>; clock-frequency = <400000>; status = "okay"; + + /* The touchpad HID device is emulated by EC so it's always present */ + touchpad: touchpad@56 { + compatible = "hid-over-i2c"; + reg = <0x56>; + hid-descr-addr = <0x0001>; + interrupt-parent = <&pio>; + interrupts = <148 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&touchpad_int>; + wakeup-source; + }; }; &i2c5 { @@ -1136,6 +1148,14 @@ }; }; + touchpad_int: touchpad-int-pins { + pins-ec-ap-touchpad-int-odl { + pinmux = ; + input-enable; + bias-disable; + }; + }; + uart0_pins: uart0-pins { pins-bus { pinmux = , From 3090c618e817e599825202e7a3110080278e08f6 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 22:30:54 +0800 Subject: [PATCH 504/521] AOSCOS: of: mark a few custom Armbian functions as static The following functions were never declared nor used by anything other than the Device Tree Overlay's ConfigFS interface. Mark them as static to resolve a few `-Werror=missing-prototypes' errors (CONFIG_WERROR). - fdt_translate_address() - ssize_t cfs_overlay_item_dtbo_read() - ssize_t cfs_overlay_item_dtbo_write() Fixes: "ARMBIAN: drv:of: Device Tree Overlay ConfigFS interface" Signed-off-by: Mingcong Bai --- drivers/of/configfs.c | 4 ++-- drivers/of/fdt_address.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c index 87974779561fc9..f4f1e1e5135c23 100644 --- a/drivers/of/configfs.c +++ b/drivers/of/configfs.c @@ -115,7 +115,7 @@ static struct configfs_attribute *cfs_overlay_attrs[] = { NULL, }; -ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, +static ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, void *buf, size_t max_count) { struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); @@ -138,7 +138,7 @@ ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, return overlay->dtbo_size; } -ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, +static ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, const void *buf, size_t count) { struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c index e1353d7a58e6ea..f358d2c807540e 100644 --- a/drivers/of/fdt_address.c +++ b/drivers/of/fdt_address.c @@ -147,7 +147,7 @@ static int __init fdt_translate_one(const void *blob, int parent, * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things */ -u64 __init fdt_translate_address(const void *blob, int node_offset) +static u64 __init fdt_translate_address(const void *blob, int node_offset) { int parent, len; const struct of_bus *bus, *pbus; From 5ce6eb325b601836d9259827168d45b9d8b1d1f3 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 22:38:08 +0800 Subject: [PATCH 505/521] AOSCOS: platform/x86: int3472: fix an incorrect number conversation specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building with CONFIG_WERROR: drivers/platform/x86/intel/int3472/discrete.c:385:44: error: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘long unsigned int’ [-Werror=format=] 385 | dev_info(int3472->dev, " con_id=%s, flags=0x%x\n", con_id, gpio_flags); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ Correct the number conversation specification as `%lx'. Fixes: "BACKPORT: SURFACE: Add camera support for Surface Pro 9" Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/discrete.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index b2a54733df2021..355b2d5af737be 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -389,7 +389,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case 0x08: /* Surface Pro 9 - treat as power*/ dev_info(int3472->dev, "GPIO type 0x%02x detected on pin 0x%02x\n", type, agpio->pin_table[0]); - dev_info(int3472->dev, " con_id=%s, flags=0x%x\n", con_id, gpio_flags); + dev_info(int3472->dev, " con_id=%s, flags=0x%lx\n", con_id, gpio_flags); ret = skl_int3472_register_regulator(int3472, gpio, GPIO_REGULATOR_ENABLE_TIME, con_id, NULL); From 40d87a1f1b8047e73bd99a55cea1f9e35fb3757a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 22:39:35 +0800 Subject: [PATCH 506/521] AOSCOS: platform/x86: int3472: remove an unused argument to dev_err() When building with CONFIG_WERROR: drivers/platform/x86/intel/int3472/discrete.c:391:47: error: too many arguments for format [-Werror=format-extra-args] 391 | dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", type, ret); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Remove the unused `type' argument to fix build. Fixes: "BACKPORT: SURFACE: Add camera support for Surface Pro 9" Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/discrete.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 355b2d5af737be..c1d6c5121a5004 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -395,7 +395,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, con_id, NULL); dev_info(int3472->dev, " register_regulator returned: %d\n", ret); if (ret) { - dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", type, ret); + dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", ret); } break; case 0x11: From a053bb85afd80a0107d604f5e68f5c3fb2325381 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 23:01:20 +0800 Subject: [PATCH 507/521] AOSCOS: drm: amdgpu: disable Panel Self Refresh v1 and v2 (PSR-SU), and Panel Replay There has been a lot of report about freezing displays on laptops, especially on those based on RDNA 2/3/4 architectures. These features are probably more trouble than they are worth, so disable them by default. Signed-off-by: Mingcong Bai --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 26b48c0100978b..53d196ef53a43c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -863,7 +863,8 @@ module_param_named(dcfeaturemask, amdgpu_dc_feature_mask, uint, 0444); * DOC: dcdebugmask (uint) * Display debug options. See enum DC_DEBUG_MASK in drivers/gpu/drm/amd/include/amd_shared.h. */ -MODULE_PARM_DESC(dcdebugmask, "all debug options disabled (default))"); +uint dcdebugmask = 0x610; +MODULE_PARM_DESC(dcdebugmask, "Disable debug options (default: disable Panel Self Refresh v1 and PSR-SU, or v2, as well as Panel Replay)"); module_param_named(dcdebugmask, amdgpu_dc_debug_mask, uint, 0444); MODULE_PARM_DESC(visualconfirm, "Visual confirm (0 = off (default), 1 = MPO, 5 = PSR)"); From 8f2f5e18eafc40a2bf7edec78ca6f7abf376fd69 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 8 May 2026 09:55:19 +0800 Subject: [PATCH 508/521] AOSCOS: mei: me: use PCI_DEVICE_DATA macro for linux-surface additions With commit 09033be12a82 ("mei: me: use PCI_DEVICE_DATA macro"), calls to the `MEI_PCI_DEVICE' macro were replaced with `PCI_DEVICE_DATA'. Convert entries added by linux-surface patches to fix build. Fixes: "BACKPORT: SURFACE: mei: me: Add Icelake device ID for iTouch" Signed-off-by: Mingcong Bai --- drivers/misc/mei/hw-me-regs.h | 5 +---- drivers/misc/mei/pci-me.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 0436998e8126d1..513ddb26c11a7b 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -92,12 +92,9 @@ #define PCI_DEVICE_ID_INTEL_MEI_CDF 0x18D3 /* Cedar Fork */ #define PCI_DEVICE_ID_INTEL_MEI_ICP_LP 0x34E0 /* Ice Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ #define PCI_DEVICE_ID_INTEL_MEI_ICP_N 0x38E0 /* Ice Lake Point N */ -#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ -#define MEI_DEV_ID_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ -#define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ - #define PCI_DEVICE_ID_INTEL_MEI_JSP_N 0x4DE0 /* Jasper Lake Point N */ #define PCI_DEVICE_ID_INTEL_MEI_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index d97945f5b8683b..e5063149c47d3f 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -98,12 +98,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP_3, MEI_ME_PCH12_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP_3, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, From 17d42910044cf6f5de8e7e62842251f1e006ca09 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 17 May 2026 12:00:22 +0800 Subject: [PATCH 509/521] AOSCOS: powerpc: define __LITTLE_ENDIAN and __BIG_ENDIAN for math-emu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to commit b929926f01f2 ("sh: define __BIG_ENDIAN for math-emu"), define __LITTLE_ENDIAN and __BIG_ENDIAN as 0 to mitigate build-time warnings: ./include/math-emu/double.h:59:21: error: ‘__BIG_ENDIAN’ is not defined, evaluates to ‘0’ [-Werror=undef] 59 | #if __BYTE_ORDER == __BIG_ENDIAN | Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202507301656.7FEX6J5W-lkp@intel.com/ Signed-off-by: Mingcong Bai --- arch/powerpc/include/asm/sfp-machine.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/sfp-machine.h b/arch/powerpc/include/asm/sfp-machine.h index 8b957aabb826d3..db8525605c026d 100644 --- a/arch/powerpc/include/asm/sfp-machine.h +++ b/arch/powerpc/include/asm/sfp-machine.h @@ -319,10 +319,12 @@ #define abort() \ return 0 -#ifdef __BIG_ENDIAN +#ifdef __BIG_ENDIAN__ #define __BYTE_ORDER __BIG_ENDIAN +#define __LITTLE_ENDIAN 0 #else #define __BYTE_ORDER __LITTLE_ENDIAN +#define __BIG_ENDIAN 0 #endif /* Exception flags. */ From 5d48f0038cba1e44bbdd2f338c7b116db0c2404a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 24 May 2026 21:06:23 +0800 Subject: [PATCH 510/521] AOSCOS: rust: Add -fpatchable-function-entry= to bindgen_skip_c_flags This flag was introduced with commit eec37961a56a ("powerpc64/ftrace: Move ftrace sequence out of line") to workaround an issue with ftrace. However, this flag was passed to bindgen and caused the following error: Unable to generate bindings: clang diagnosed error: error: unsupported option '-fpatchable-function-entry=1' for target 'powerpc64le-unknown-linux-gnu' Add this flag to bindgen_skip_c_flags to fix build. Fixes: "BACKPORT: FROMLIST: powerpc: Enable Rust for ppc64le" Tested-by: Mingcong Bai --- rust/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Makefile b/rust/Makefile index 5f3946d1792129..cbf8289d7de0d9 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -397,7 +397,7 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ -fzero-init-padding-bits=% -mno-fdpic \ -fdiagnostics-show-context -fdiagnostics-show-context=% \ --param=% --param asan-% -fno-isolate-erroneous-paths-dereference \ - -mno-riscv-attribute + -mno-riscv-attribute -fpatchable-function-entry=% # All warnings are inhibited since GCC builds are very experimental, # many GCC warnings are not supported by Clang, they may only appear in From a89c9e4341931b705c43663133fc8c8f0e87181c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 16:05:51 +0800 Subject: [PATCH 511/521] AOSCOS: MIPS: select ARCH_FORCE_MAX_ORDER >= 11 if NUMA_BALANCING is selected This should suppress an assertion failure during build. Further investigation should be conducted when possible. Fixes: "BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4de8bafd90fbd7..3ff89b82e2d7be 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2139,9 +2139,9 @@ config ZBOOT_LOAD_ADDRESS config ARCH_FORCE_MAX_ORDER int "Maximum zone order" - default "13" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_64KB - default "12" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_32KB - default "11" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_16KB + default "13" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_64KB + default "12" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_32KB + default "11" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_16KB default "10" help The kernel memory allocator divides physically contiguous memory From 8f540eb873eec4465b5a80b1a8a9953a2ee0c221 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 16:07:57 +0800 Subject: [PATCH 512/521] AOSCOS: init: make NUMA_BALANCING depend on TRANSPARENT_HUGEPAGE if MIPS This should suppress an assertion failure during build. Further investigation should be conducted when possible. Fixes: "BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index 826a7d768ca368..32c84c02c88d3a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1012,6 +1012,7 @@ config NUMA_BALANCING depends on ARCH_SUPPORTS_NUMA_BALANCING depends on !ARCH_WANT_NUMA_VARIABLE_LOCALITY depends on SMP && NUMA_MIGRATION && !PREEMPT_RT + depends on TRANSPARENT_HUGEPAGE || !MIPS help This option adds support for automatic NUMA aware memory/task placement. The mechanism is quite primitive and is based on migrating memory when From 0c8eef3a5c150f26c12e17d2d3eb4c2e59e5e034 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 16:36:25 +0800 Subject: [PATCH 513/521] AOSCOS: MIPS: audit: declare function prototypes for audit_classify_syscall_*() Functions `audit_classify_syscall_n32()', `audit_classify_syscall_o32()` were never declared in the previous code and causes build errors due to `-Werror=missing-prototype'. Create function prototype declarations in `audit-{n,o}32.h' and reference them in each of the respective sources. Also remove the extern references as they are no longer needed. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/audit-n32.c | 1 + arch/mips/kernel/audit-n32.h | 5 +++++ arch/mips/kernel/audit-native.c | 5 ++--- arch/mips/kernel/audit-o32.c | 1 + arch/mips/kernel/audit-o32.h | 5 +++++ 5 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 arch/mips/kernel/audit-n32.h create mode 100644 arch/mips/kernel/audit-o32.h diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 2248badc3acb65..2e9ac29588c810 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -4,6 +4,7 @@ #include #include #include +#include "audit-n32.h" static unsigned dir_class_n32[] = { #include diff --git a/arch/mips/kernel/audit-n32.h b/arch/mips/kernel/audit-n32.h new file mode 100644 index 00000000000000..781de2e9ad66be --- /dev/null +++ b/arch/mips/kernel/audit-n32.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef CONFIG_AUDITSYSCALL_N32 +int audit_classify_syscall_n32(int abi, unsigned syscall); +#endif /* CONFIG_AUDITSYSCALL_N32 */ diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 09ae3dbcfecbcc..88e283f8df3850 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -2,6 +2,8 @@ #include #include #include +#include "audit-n32.h" +#include "audit-o32.h" static unsigned dir_class[] = { #include @@ -37,9 +39,6 @@ int audit_classify_arch(int arch) return 0; } -extern int audit_classify_syscall_o32(int abi, unsigned syscall); -extern int audit_classify_syscall_n32(int abi, unsigned syscall); - int audit_classify_syscall(int abi, unsigned syscall) { int res; diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c index e8b9b50f7d2671..6bf302c04e5d3b 100644 --- a/arch/mips/kernel/audit-o32.c +++ b/arch/mips/kernel/audit-o32.c @@ -4,6 +4,7 @@ #include #include #include +#include "audit-o32.h" static unsigned dir_class_o32[] = { #include diff --git a/arch/mips/kernel/audit-o32.h b/arch/mips/kernel/audit-o32.h new file mode 100644 index 00000000000000..8a782fdd1c6e78 --- /dev/null +++ b/arch/mips/kernel/audit-o32.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef CONFIG_AUDITSYSCALL_O32 +int audit_classify_syscall_o32(int abi, unsigned syscall); +#endif /* CONFIG_AUDITSYSCALL_O32 */ From 755f2d91d49c55e9ec2c27e7e2d656ffb407e65e Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 16:39:13 +0800 Subject: [PATCH 514/521] AOSCOS: MIPS: audit: replace magic audit syscall class numbers with macros Follow commit 42f355ef59a2 ("audit: replace magic audit syscall class numbers with macros") and replace all magic numbers in MIPS audit code. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/audit-n32.c | 6 +++--- arch/mips/kernel/audit-native.c | 8 ++++---- arch/mips/kernel/audit-o32.c | 10 +++++----- include/linux/audit_arch.h | 3 +++ kernel/auditsc.c | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 2e9ac29588c810..8d114bf8542548 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -35,11 +35,11 @@ int audit_classify_syscall_n32(int abi, unsigned syscall) { switch (syscall) { case __NR_open: - return 2; + return AUDITSC_OPEN; case __NR_openat: - return 3; + return AUDITSC_OPENAT; case __NR_execve: - return 5; + return AUDITSC_EXECVE; default: return 0; } diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 88e283f8df3850..4a0fecd0e48674 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -45,20 +45,20 @@ int audit_classify_syscall(int abi, unsigned syscall) switch (syscall) { case __NR_open: - res = 2; + res = AUDITSC_OPEN; break; case __NR_openat: - res = 3; + res = AUDITSC_OPEN; break; #ifdef __NR_socketcall /* Only exists on O32 */ case __NR_socketcall: - res = 4; + res = AUDITSC_SOCKETCALL; break; #endif case __NR_execve: - res = 5; + res = AUDITSC_EXECVE; break; default: #ifdef CONFIG_AUDITSYSCALL_O32 diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c index 6bf302c04e5d3b..818f91ce14acf3 100644 --- a/arch/mips/kernel/audit-o32.c +++ b/arch/mips/kernel/audit-o32.c @@ -35,15 +35,15 @@ int audit_classify_syscall_o32(int abi, unsigned syscall) { switch (syscall) { case __NR_open: - return 2; + return AUDITSC_OPEN; case __NR_openat: - return 3; + return AUDITSC_OPENAT; case __NR_socketcall: - return 4; + return AUDITSC_SOCKETCALL; case __NR_execve: - return 5; + return AUDITSC_EXECVE; default: - return 0; + return AUDITSC_NATIVE; } } diff --git a/include/linux/audit_arch.h b/include/linux/audit_arch.h index 2b8153791e6a5d..2594c9b1c78917 100644 --- a/include/linux/audit_arch.h +++ b/include/linux/audit_arch.h @@ -17,6 +17,9 @@ enum auditsc_class_t { AUDITSC_SOCKETCALL, AUDITSC_EXECVE, AUDITSC_OPENAT2, +#ifdef CONFIG_MIPS + AUDITSC_MIPS_N32, +#endif AUDITSC_NVALS /* count */ }; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 2971f0d2574ac3..680b1db10aedb5 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -190,7 +190,7 @@ static int audit_match_perm(struct audit_context *ctx, int mask) case AUDITSC_OPENAT2: return mask & ACC_MODE((u32)ctx->openat2.flags); #ifdef CONFIG_MIPS - case 6: /* for N32 */ + case AUDITSC_MIPS_N32: /* for N32 */ if ((mask & AUDIT_PERM_WRITE) && audit_match_class(AUDIT_CLASS_WRITE_N32, n)) return 1; From 87027640d50ab676f2c7c8ffc2edea5f6e359d78 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 17 Dec 2024 02:00:03 +0800 Subject: [PATCH 515/521] AOSCOS: MIPS: audit: clean up the last remnants of magic numbers Follow commit 42f355ef59a2 ("audit: replace magic audit syscall class numbers with macros") and replace all magic numbers in MIPS audit code. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/audit-n32.c | 2 +- arch/mips/kernel/audit-native.c | 33 +++++++-------------------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 8d114bf8542548..76c95ff924f83b 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -41,7 +41,7 @@ int audit_classify_syscall_n32(int abi, unsigned syscall) case __NR_execve: return AUDITSC_EXECVE; default: - return 0; + return AUDITSC_NATIVE; } } diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 4a0fecd0e48674..763f0e6b3636aa 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -41,45 +41,26 @@ int audit_classify_arch(int arch) int audit_classify_syscall(int abi, unsigned syscall) { - int res; - switch (syscall) { case __NR_open: - res = AUDITSC_OPEN; - break; - + return AUDITSC_OPEN; case __NR_openat: - res = AUDITSC_OPEN; - break; - + return AUDITSC_OPEN; #ifdef __NR_socketcall /* Only exists on O32 */ case __NR_socketcall: - res = AUDITSC_SOCKETCALL; - break; + return AUDITSC_SOCKETCALL; #endif case __NR_execve: - res = AUDITSC_EXECVE; - break; + return AUDITSC_EXECVE; default: #ifdef CONFIG_AUDITSYSCALL_O32 - res = audit_classify_syscall_o32(abi, syscall); - if (res) - break; + return audit_classify_syscall_o32(abi, syscall); #endif #ifdef CONFIG_AUDITSYSCALL_N32 - res = audit_classify_syscall_n32(abi, syscall); - if (res) - break; + return audit_classify_syscall_n32(abi, syscall); #endif - if (abi == AUDIT_ARCH_MIPS || abi == AUDIT_ARCH_MIPSEL) - res = 1; - else if (abi == AUDIT_ARCH_MIPS64 || abi == AUDIT_ARCH_MIPSEL64) - res = 0; - else if (abi == AUDIT_ARCH_MIPS64N32 || abi == AUDIT_ARCH_MIPSEL64N32) - res = 6; + return AUDITSC_NATIVE; } - - return res; } static int __init audit_classes_init(void) From 921331ca60d031d238b8ee9e0918e312c730fbc2 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 15 Dec 2024 11:21:54 +0800 Subject: [PATCH 516/521] AOSCOS: audit: remove duplicate functions for MIPS audit_classify_arch() and audit_classify_syscall() are already defined in arch/mips/kernel/audit-native.c, adding preprocessor directives to remove them from lib/audit.c on MIPS. Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- lib/audit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/audit.c b/lib/audit.c index bc07fbd3a6983f..7063bd38f2c635 100644 --- a/lib/audit.c +++ b/lib/audit.c @@ -29,6 +29,7 @@ static unsigned signal_class[] = { ~0U }; +#if !defined(CONFIG_MIPS) int audit_classify_arch(int arch) { if (audit_is_compat(arch)) @@ -68,6 +69,7 @@ int audit_classify_syscall(int abi, unsigned syscall) return AUDITSC_NATIVE; } } +#endif static int __init audit_classes_init(void) { From ff7579f60452c2eb7e2976c023b6efc9df77d62d Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Fri, 12 Jun 2026 23:32:42 +0800 Subject: [PATCH 517/521] AOSCOS: drm/ttm: fix incorrect caching type caused by a previous AOSCOS patch The ttm_mem_io_reserve() function calls a driver-defined callback to set mem->bus.caching. Thus we cannot use the value read before calling ttm_mem_io_reserve(), or we'd effectively erase the set from driver. Instead we should only downgrade cached to write_combined if the driver says to use cached. Fixes: eb4186e62799 ("BACKPORT: FROMLIST: drm/ttm: downgrade cached to write_combined when snooping not available") Signed-off-by: Xi Ruoyao --- drivers/gpu/drm/ttm/ttm_resource.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 9ba711790a9528..6cd19a877b2ce4 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -848,9 +848,12 @@ ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io, struct ttm_device *bdev, struct ttm_resource *mem) { - enum ttm_caching caching = mem->bus.caching; + enum ttm_caching caching; int ret; + ret = ttm_mem_io_reserve(bdev, mem); + caching = mem->bus.caching; + #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -859,7 +862,6 @@ ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io, caching = ttm_write_combined; #endif - ret = ttm_mem_io_reserve(bdev, mem); if (ret) goto out_err; if (!mem->bus.is_iomem) { From 814f97de3335dbab5d96868bf2ac4c97facc2490 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:39:08 +0800 Subject: [PATCH 518/521] AOSCOS: net/phytium: allow building the driver everywhere if CONFIG_COMPILE_TEST Also add a guard for dma_ops assignment as it only exists on the architectures selecting CONFIG_ARCH_HAS_DMA_OPS. It makes hacking the driver without an aarch64 machine easier. Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/phytium/Kconfig | 4 ++-- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index 74f671b5e0d416..4ba96e11a59490 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_PHYTIUM bool "Phytium devices" - depends on ARCH_PHYTIUM + depends on ARCH_PHYTIUM || COMPILE_TEST depends on HAS_IOMEM default y help @@ -21,7 +21,7 @@ if NET_VENDOR_PHYTIUM config PHYTMAC tristate "Phytium GMAC support" depends on ACPI - depends on ARCH_PHYTIUM + depends on ARCH_PHYTIUM || COMPILE_TEST depends on HAS_DMA select PHYLINK select CRC32 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 18950063f37200..d769da9ac3cc9f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -176,7 +176,9 @@ static int phytium_dwmac_probe(struct platform_device *pdev) * show kernel error 'DMA descriptors allocation failed' */ if (acpi_match_device_ids(to_acpi_device(&pdev->dev), phytium_old_acpi_id)) { +#ifdef CONFIG_ARCH_HAS_DMA_OPS pdev->dev.dma_ops = NULL; // solved set DMA mask Failed +#endif plat->host_dma_width = 32; } #endif From 2e274aeb8d39ec87d328a7afab8b5961e6d042ef Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:40:17 +0800 Subject: [PATCH 519/521] AOSCOS: net/phytium: drop of_gpio.h inclusion This seems used nowhere and the header is removed in Linux 7.1. Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/phytium/phytmac_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 3f1f2098284de9..0d6bdb44cf516b 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include From 7e022d5129a22e915d4dea84a2ad52c6a399db2e Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:41:29 +0800 Subject: [PATCH 520/521] AOSCOS: net/phytium: remove plat_dat->port_node Link: https://git.kernel.org/torvalds/c/44a2ec96d374 Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index d769da9ac3cc9f..3c19f212704195 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -91,10 +91,8 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->phy_interface = plat->phy_interface; /* Configure PHY if using device-tree */ - if (pdev->dev.of_node) { + if (pdev->dev.of_node) plat->phy_node = of_parse_phandle(np, "phy-handle", 0); - plat->port_node = of_fwnode_handle(np); - } if (pdev->dev.of_node) { plat->bus_id = of_alias_get_id(np, "ethernet"); From de445861c4545101031d3da982b32d6a34d38805 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:57:39 +0800 Subject: [PATCH 521/521] AOSCOS: net/phytium: guard "np" variable definition with #ifdef CONFIG_OF The of_match_node macro is no-op if !CONFIG_OF, so with CONFIG_WERROR the build would fail as nothing uses the "np" variable. Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/phytium/phytmac_platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 3724936cc826e2..225ba0835df927 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -168,7 +168,9 @@ static int phytmac_get_phy_mode(struct platform_device *pdev) static int phytmac_plat_probe(struct platform_device *pdev) { const struct phytmac_config *phytmac_config = &phytium_1p0_config; +#ifdef CONFIG_OF struct device_node *np; +#endif struct resource *regs; struct phytmac *pdata; int ret, i;