| Message ID | 20260702214750.3428694-3-utilityemal77@gmail.com (mailing list archive) |
|---|---|
| State | New |
| Headers |
Return-Path: <linux-sunxi+bounces-24095-sunxi=pue.re@lists.linux.dev>
X-Original-To: noreply@patchwork.local
Delivered-To: noreply@patchwork.local
Received: from tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114])
by mxe881.netcup.net (Postfix) with ESMTPS id 850891C00A4
for <noreply@patchwork.local>; Thu, 2 Jul 2026 23:48:08 +0200 (CEST)
Authentication-Results: mxe881;
dkim=pass header.d=gmail.com;
spf=pass (sender IP is 172.105.105.114)
smtp.mailfrom=linux-sunxi+bounces-24095-noreply=patchwork.local@lists.linux.dev
smtp.helo=tor.lore.kernel.org
Received-SPF: pass (mxe881: domain of lists.linux.dev designates
172.105.105.114 as permitted sender) client-ip=172.105.105.114;
envelope-from=linux-sunxi+bounces-24095-noreply=patchwork.local@lists.linux.dev;
helo=tor.lore.kernel.org;
Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org
[100.90.174.1])
by tor.lore.kernel.org (Postfix) with ESMTP id 2B82E3015869
for <noreply@patchwork.local>; Thu, 2 Jul 2026 21:48:07 +0000 (UTC)
Received: from localhost.localdomain (localhost.localdomain [127.0.0.1])
by smtp.subspace.kernel.org (Postfix) with ESMTP id ED539312834;
Thu, 2 Jul 2026 21:48:06 +0000 (UTC)
Authentication-Results: smtp.subspace.kernel.org;
dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com
header.b="myJvm1/9"
X-Original-To: linux-sunxi@lists.linux.dev
Received: from mail-yx1-f52.google.com (mail-yx1-f52.google.com
[74.125.224.52])
(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))
(No client certificate requested)
by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1875638B12A
for <linux-sunxi@lists.linux.dev>; Thu, 2 Jul 2026 21:48:03 +0000 (UTC)
Authentication-Results: smtp.subspace.kernel.org;
arc=none smtp.client-ip=74.125.224.52
ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;
t=1783028886; cv=none;
b=Ck1kLNL4aIFBNV86leG3ZsPKJAniI+pbQtLnSiRUu4eMLUkyjRGu53FexZd6oT0UNO8kpjX3idM3lwbE+i1B0c6WtbvMyHaCzsyLcKMAjVkVMvSP5PXhroeR7KJRZkDDaT2i+F6gKNzjoyw3tLXTr69kBBY5v6BXBCZou9PfJ1c=
ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org;
s=arc-20240116; t=1783028886; c=relaxed/simple;
bh=Be/cWPQ59xpS4zI4d7uyaSUlPTVzJY+oz2ilnWbcb+Q=;
h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:
MIME-Version:Content-Type;
b=cpEJSGQByQLs35RKBHeF+m07BFqISNySbTBwIqATZsnPM6dytXrf4Q010fIz1YEnttq/4MJ5ekuR5M3RR6Mr3oL9EB4gYUy6y34a/ZFewm8jTVyYBf4uCAjYH22awMJUrJOm2khvHGGPuWromFkI1ryCTHl3curRipxNYydWtM0=
ARC-Authentication-Results: i=1; smtp.subspace.kernel.org;
dmarc=pass (p=none dis=none) header.from=gmail.com;
spf=pass smtp.mailfrom=gmail.com;
dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com
header.b=myJvm1/9; arc=none smtp.client-ip=74.125.224.52
Authentication-Results: smtp.subspace.kernel.org;
dmarc=pass (p=none dis=none) header.from=gmail.com
Authentication-Results: smtp.subspace.kernel.org;
spf=pass smtp.mailfrom=gmail.com
Received: by mail-yx1-f52.google.com with SMTP id
956f58d0204a3-664eb8cb631so2060651d50.3
for <linux-sunxi@lists.linux.dev>;
Thu, 02 Jul 2026 14:48:03 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=20251104; t=1783028883; x=1783633683;
darn=lists.linux.dev;
h=content-transfer-encoding:content-type:mime-version:references
:in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject
:date:message-id:reply-to:content-type;
bh=b5Ik/jaiyxGq/K61KC4YWGXGDB1yT8F48WMWIyoKQXM=;
b=myJvm1/9+D7ceJjAbIBN4JL9J545TFs/tkpgPORHpabIxg/bxge2+5PKuUj7PlIG7l
A/SERJzOkM/byP5RLOsdd/atohv2eH7fen1TgFAvqcNFGa44xUI5IQqmjn24PcJcWELA
EczrTdooY7qnPh5Zufm3/YvZ6ew8uV8L2vQ54KpooY2nfxHXdwlf7qwK317Iw9IXSKij
/xhuGnNwEYRmvsm644mKSjAVHnHrpmMfoGHfN634NXHM7M1M31U9UkvQaS/lktuM4K9t
xSeu0RtufAYm7E1Z3f+FjX58rxQ3yWiEQs8Dp4DEG0UtjYzLqfLqS2FLg7JzSppiOggp
Iiew==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20251104; t=1783028883; x=1783633683;
h=content-transfer-encoding:content-type:mime-version:references
:in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg
:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to
:content-type;
bh=b5Ik/jaiyxGq/K61KC4YWGXGDB1yT8F48WMWIyoKQXM=;
b=Ma6iZnGMLKuWICPRug2NJgoAXTUZgIm6DPEWrxTeP56HbOgbLCTu/FONhGMO8tbQl8
SQa8OpE/W3NTSMy8iGb7kXIzO0UdOdEEDTq4F+TS6T3SenOXRiQ1tn2BE64pF6Z2yXVH
6MI2KJepGLE5LpDP934YNBJBKfcaDMyDDQMndhvrGx8LfwFmxPWGKHUxiUaC7KD9Pihz
OzhI4zvQv3Drooy+hx8MmAgXmMRktGnPVF2FnOGBUnLCnWUn5bgZRE7c8HV3NKyM+xyT
Gt7rtQyoZtS0gxao+h5AmHQ/oSCpzkYdGsrZVvs5TuktMXt0vae/tUszH4CbsWEU+ptH
Lj4Q==
X-Forwarded-Encrypted: i=1;
AHgh+RoCmWjY1L7/FAfP4vZcOwK60Op5mW9teqge77fw4EpPzYVIcy7K8hAElzdU+nXrdfylSbOOc8KnEHUD0w==@lists.linux.dev
X-Gm-Message-State: AOJu0YyeioisxRf6PyHikFwDwkZE3cHtMjycwZHSdU6xmlmnvIuAJY2f
Ne7dZrSoLdgqOGDKf8kEfFt7Bpg5H9GB/4cqeiiIpBQWoSO5rKbCVGkw
X-Gm-Gg: AfdE7cnZmUEcH2Bw2tadSGskFEj/HDProhnsEy/8jjtTsxIkzl1g8QzasBzK1jZbI7u
6zOExXe3tK70Vhq4iBF8nUE8VDbATjTBiiFiLfGNg0AvgWPqzZWCJ13x40Ohi25Dmz6n/rn9jZx
tMjMjyO7+AioPvJcYGVoATwyUWsv8hyhoLcxfJU1XTXsq+zPQFFEjyVPHkYcYUUJ8zlAcoh+EPR
cipqnXLNUrKvECz/PNuYA0wU+CWwyzHBiuL0Cueb1xKrONZ3ZQXUpE0iBq17Ht1QaEI/DBMgsnJ
oqwpTk+CqF05dtof3vQYGJhzS5U2HUJ1/qxXvisDnvIa0x8XqTSnOaEqpVZWphm9N+yUGEAg11X
q19J799c/qTGzot8ed53qLKm2TdQqHQ0CLZ9c7aH8De+yPKhmX9j/D+bwYyiAfUEkq80+MNCIWQ
rbmiHyfrI5AB052PLU+NxUv22Gk5UB9ShpWF8AZzVjdSzT25s=
X-Received: by 2002:a05:690e:4089:b0:664:ae6a:ef2 with SMTP id
956f58d0204a3-66596695a62mr6927006d50.80.1783028882666;
Thu, 02 Jul 2026 14:48:02 -0700 (PDT)
Received: from zenbox ([2600:1700:18fb:6011:169a:36bf:d0a4:1d7c])
by smtp.gmail.com with ESMTPSA id
956f58d0204a3-66624021a75sm1631772d50.11.2026.07.02.14.48.01
(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
Thu, 02 Jul 2026 14:48:02 -0700 (PDT)
From: Justin Suess <utilityemal77@gmail.com>
To: Sean Young <sean@mess.org>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
Chen-Yu Tsai <wens@kernel.org>,
Jernej Skrabec <jernej.skrabec@gmail.com>,
Samuel Holland <samuel@sholland.org>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Maxime Ripard <mripard@kernel.org>
Cc: linux-media@vger.kernel.org,
devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-sunxi@lists.linux.dev,
Sashiko <sashiko-bot@kernel.org>,
Justin Suess <utilityemal77@gmail.com>
Subject: [PATCH 2/4] media: rc: sunxi-cir: add support for the A523
Date: Thu, 2 Jul 2026 17:47:48 -0400
Message-ID: <20260702214750.3428694-3-utilityemal77@gmail.com>
X-Mailer: git-send-email 2.54.0
In-Reply-To: <20260702214750.3428694-1-utilityemal77@gmail.com>
References: <20260702214750.3428694-1-utilityemal77@gmail.com>
Precedence: bulk
X-Mailing-List: linux-sunxi@lists.linux.dev
List-Id: <linux-sunxi.lists.linux.dev>
List-Subscribe: <mailto:linux-sunxi+subscribe@lists.linux.dev>
List-Unsubscribe: <mailto:linux-sunxi+unsubscribe@lists.linux.dev>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
X-Rspamd-Server: rspamd-worker-8404
X-Spamd-Result: default: False [-0.16 / 15.00];
BAYES_HAM(-5.50)[100.00%];
RBL_SENDERSCORE(2.00)[172.105.105.114:from];
SUSPICIOUS_RECIPS(1.50)[];
MID_CONTAINS_FROM(1.00)[];
DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed),
No valid DKIM,none];
MAILLIST(-0.15)[generic];
BAD_REP_POLICIES(0.10)[];
MIME_GOOD(-0.10)[text/plain];
HAS_LIST_UNSUB(-0.01)[];
RCPT_COUNT_TWELVE(0.00)[15];
FREEMAIL_FROM(0.00)[gmail.com];
DBL_BLOCKED_OPENRESOLVER(0.00)[tor.lore.kernel.org:rdns,tor.lore.kernel.org:helo];
TO_DN_SOME(0.00)[];
FUZZY_BLOCKED(0.00)[rspamd.com];
FORGED_SENDER_MAILLIST(0.00)[];
FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,lists.linux.dev,kernel.org,gmail.com];
FREEMAIL_TO(0.00)[mess.org,kernel.org,gmail.com,sholland.org];
TAGGED_RCPT(0.00)[dt];
FROM_HAS_DN(0.00)[];
RCVD_TLS_LAST(0.00)[];
ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1];
MIME_TRACE(0.00)[0:+];
FORGED_RECIPIENTS_MAILLIST(0.00)[];
PRECEDENCE_BULK(0.00)[];
ASN(0.00)[asn:63949, ipnet:172.105.96.0/20, country:SG];
FROM_NEQ_ENVFROM(0.00)[utilityemal77@gmail.com,linux-sunxi@lists.linux.dev];
TAGGED_FROM(0.00)[bounces-24095-noreply=patchwork.local];
R_SPF_ALLOW(0.00)[+ip4:172.105.105.114];
RCVD_COUNT_FIVE(0.00)[6];
RCVD_VIA_SMTP_AUTH(0.00)[]
X-Rspamd-Queue-Id: 850891C00A4
X-MORS-Enabled: yes
X-MORS-DOMAIN: patchwork.local
X-MORS-HOSTING: hosting172546
X-MORS-USER: hosting172546
X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?=
|
| Series |
media: rc: sunxi-cir: support the A523/H728/T527 IR receiver
|
|
Commit Message
Justin Suess
July 2, 2026, 9:47 p.m. UTC
The A523 (sun55i) has a newer revision of the CIR receiver IP. Two
register fields that do not exist on older SoCs must be programmed
for reception to work:
- CTL bits [7:6] select which pulse polarities are captured into the
RX FIFO. The reset value of 0 captures nothing, so program "both
pulse" mode, which captures regardless of header polarity.
- SPLCFG (the sample configuration register) bits [1:0] select the
sample clock as a division of the module clock, replacing the
fixed module clock / 64 sample rate of the older IP. Select
module clock / 256, which together with the 24 MHz module clock
used on the A523 gives a 10.7 μs sample period, close to the 8 μs
of the previous 8 MHz / 64 configuration, and keeps the default
125 ms idle timeout representable in the 8-bit idle threshold
field.
Parameterize the sample divisor in the resolution/timeout
calculations, which older SoCs keep at the fixed 64, and add the
A523 quirks and compatible.
Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
drivers/media/rc/sunxi-cir.c | 76 ++++++++++++++++++++++++++++++------
1 file changed, 63 insertions(+), 13 deletions(-)
Comments
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: Pre-existing issues: - [High] Incorrect device registration order in probe and missing `rc_unregister_device` on failure leads to race conditions and Use-After-Free. - [High] Rounding mismatch in timeout calculations causes the maximum timeout to wrap around to 0, breaking IR reception. - [Medium] Malformed `clock-frequency` causes a division by zero kernel panic during timeout calculations. - [Medium] Inconsistent use of DT-requested clock frequency versus actual clock rate introduces timing inaccuracies. -- commit ca10429d8baf92c8b3413bded6e91ab88182edf2 Author: Justin Suess <utilityemal77@gmail.com> media: rc: sunxi-cir: add support for the A523 This patch adds support for the A523 (sun55i) SoC to the sunxi-cir driver. It introduces support for programming pulse polarities and a parameterized sample clock divider required by the newer IP revision. > diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c > --- a/drivers/media/rc/sunxi-cir.c > +++ b/drivers/media/rc/sunxi-cir.c [ ... ] > @@ -140,17 +165,19 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) > > /* Convert idle threshold to usec */ > -static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int ithr) > +static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int div, > + unsigned int ithr) > { > return DIV_ROUND_CLOSEST(USEC_PER_SEC * (ithr + 1), > - base_clk / (128 * 64)); > + base_clk / (128 * div)); [Severity: Medium] This is a pre-existing issue, but does this calculation risk a division by zero panic during device probe? If the clock provider rate evaluates to a value smaller than 128 * div, base_clk / (128 * div) evaluates to 0. This resulting zero is then passed as the divisor to DIV_ROUND_CLOSEST(), which would cause a panic. > } > > /* Convert usec to idle threshold */ > -static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int usec) > +static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int div, > + unsigned int usec) > { > /* make sure we don't end up with a timeout less than requested */ > - return DIV_ROUND_UP((base_clk / (128 * 64)) * usec, USEC_PER_SEC) - 1; > + return DIV_ROUND_UP((base_clk / (128 * div)) * usec, USEC_PER_SEC) - 1; > } > > static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) > { > struct sunxi_ir *ir = rc_dev->priv; > unsigned int base_clk = clk_get_rate(ir->clk); > > - unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout); > + unsigned int ithr = sunxi_usec_to_ithr(base_clk, ir->sample_divisor, > + timeout); > > dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr); > > - /* Set noise threshold and idle threshold */ > - writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), > + /* Set sample clock divider, noise threshold and idle threshold */ > + writel(REG_CIR_SDIV(ir->sample_div_sel) | > + REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), > ir->base + SUNXI_IR_CIR_REG); [Severity: High] This is a pre-existing issue, but does the rounding mismatch between sunxi_ithr_to_usec() and sunxi_usec_to_ithr() cause the maximum timeout to wrap around to 0? Because sunxi_ithr_to_usec() uses DIV_ROUND_CLOSEST, it can round the fractional value up when calculating the maximum timeout. If userspace requests this maximum timeout, sunxi_usec_to_ithr() uses DIV_ROUND_UP, which pushes the resulting threshold over the maximum of 255 to 256. Since REG_CIR_ITHR masks the value to 8 bits, passing 256 wraps it to 0 when written to the hardware, which would immediately break IR reception. [ ... ] > @@ -325,10 +363,10 @@ static int sunxi_ir_probe(struct platform_device *pdev) > ir->rc->dev.parent = dev; > ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; > /* Frequency after IR internal divider with sample period in us */ > - ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); > + ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / ir->sample_divisor)); [Severity: Medium] This is a pre-existing issue, but does this calculation introduce timing inaccuracies by using the DT-requested clock frequency instead of the actual clock rate? If the clock provider rounds the requested rate set earlier in sunxi_ir_probe(), using the raw b_clk_freq here instead of clk_get_rate(ir->clk) means the software's sample period drifts from the actual hardware timing. [Severity: High] This is a pre-existing issue, but does the registration order in sunxi_ir_probe() create a race condition and potential Use-After-Free? Looking later in sunxi_ir_probe(), rc_register_device() is called before sunxi_ir_hw_init() enables the hardware clocks: sunxi_ir_probe() { ... ret = rc_register_device(ir->rc); ... ret = sunxi_ir_hw_init(dev); ... } If userspace accesses the device via ioctl before the hardware clocks are enabled, it will access clock-gated memory and cause a bus fault. Additionally, if probe fails during hardware initialization or IRQ request, it branches to the exit_free_dev label: exit_free_dev: rc_free_device(ir->rc); This only calls rc_free_device() without calling rc_unregister_device() first. The device remains exposed to userspace while its backing driver data is freed, which could lead to a Use-After-Free memory corruption. > ir->rc->timeout = IR_DEFAULT_TIMEOUT; > - ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0); > - ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255); > + ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 0); > + ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 255); > ir->rc->s_timeout = sunxi_ir_set_timeout; > ir->rc->driver_name = SUNXI_IR_DEV;
On Thu, 2 Jul 2026 17:47:48 -0400 Justin Suess <utilityemal77@gmail.com> wrote: Hi Justin, many thanks for sending this! > The A523 (sun55i) has a newer revision of the CIR receiver IP. Two > register fields that do not exist on older SoCs must be programmed > for reception to work: > > - CTL bits [7:6] select which pulse polarities are captured into the > RX FIFO. The reset value of 0 captures nothing, so program "both > pulse" mode, which captures regardless of header polarity. Are you sure about that? The manual says that *both* the 0b00 (reset default) and 0b01 values capture both edges, and actually the H6, A133 and H616 have the same bits, and it apparently works there. I don't see those bits documented in the A64 (and earlier), but haven't checked yet whether they exist there regardless or have an effect. So I think we should force those bits either to 0 or to 1, depending on how those bits behave on A64 and before, and how compatible this is with H6, A133, H616. I will try to run some experiments on the weekend. > - SPLCFG (the sample configuration register) bits [1:0] select the > sample clock as a division of the module clock, replacing the > fixed module clock / 64 sample rate of the older IP. That's not fully correct: even the A20(!) has these two bits, actually there is a third bit, held in bit 24 (because reasons). All those bits reset to 0, which is encoded as /64, so this is where the rate comes from. And sunxi_ir_probe() sets the IR clock to 8MHz, which should end up as 24MHz / 3, on all chips, including the A523. So what is going on here? Is the manual wrong, about those bits, or the clock sources? Can you point to the BSP sources, if you used those? > module clock / 256, which together with the 24 MHz module clock Why is the A523 mod clock set to 24 MHz? You seem to do this in the DT, overriding the 8MHz default? The driver clearly has a clk_set_rate() call with that default 8MHz as an argument, and I don't think we should deviate from that, unless there are good reasons. The sample clock should be more of a driver/subsystem decision, not a a device one. > used on the A523 gives a 10.7 μs sample period, close to the 8 μs > of the previous 8 MHz / 64 configuration, and keeps the default > 125 ms idle timeout representable in the 8-bit idle threshold This is some good info that helps people understand the reasoning behind those timing values. Please put this in a comment near the top of the file. But actually: how does this compute? With an 8us sample clock period, the 8-bit ATHR field only covers 2 ms. And I don't see us setting the ATHC bit to bump this by 128. Cheers, Andre > field. > > Parameterize the sample divisor in the resolution/timeout > calculations, which older SoCs keep at the fixed 64, and add the > A523 quirks and compatible. > > Signed-off-by: Justin Suess <utilityemal77@gmail.com> > --- > drivers/media/rc/sunxi-cir.c | 76 ++++++++++++++++++++++++++++++------ > 1 file changed, 63 insertions(+), 13 deletions(-) > > diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c > index cb4c56bf0752..82ada9dc0347 100644 > --- a/drivers/media/rc/sunxi-cir.c > +++ b/drivers/media/rc/sunxi-cir.c > @@ -31,6 +31,11 @@ > /* CIR mode */ > #define REG_CTL_MD (BIT(4) | BIT(5)) > > +/* Pulse mode selector (bits [7:6]) */ > +#define REG_CTL_PMD(m) ((m) << 6) > +/* Capture both pulse polarities */ > +#define REG_CTL_PMD_BOTH REG_CTL_PMD(1) > + > /* Rx Config */ > #define SUNXI_IR_RXCTL_REG 0x10 > /* Pulse Polarity Invert flag */ > @@ -66,6 +71,13 @@ > > /* IR Sample Config */ > #define SUNXI_IR_CIR_REG 0x34 > +/* > + * Sample clock divider select (bits [1:0]), present on newer IP revisions > + * (e.g. sun55i). Selects the sample clock as a fraction of the module clock; > + * must be programmed for the sampler to run. Older SoCs lack the field and > + * use a fixed module-clock/64 sample rate, so they leave it 0. > + */ > +#define REG_CIR_SDIV(val) ((val) & GENMASK(1, 0)) > /* CIR_REG register noise threshold */ > #define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2))) > /* CIR_REG register idle threshold */ > @@ -73,6 +85,8 @@ > > /* Required frequency for IR0 or IR1 clock in CIR mode (default) */ > #define SUNXI_IR_BASE_CLK 8000000 > +/* Default sample clock divisor: module clock / 64 (legacy fixed rate) */ > +#define SUNXI_IR_SAMPLE_DIV 64 > /* Noise threshold in samples */ > #define SUNXI_IR_RXNOISE 1 > > @@ -81,10 +95,18 @@ > * > * @has_reset: SoC needs reset deasserted. > * @fifo_size: size of the fifo. > + * @both_pulse: program the CTRL pulse-mode field (newer IP revisions). As mentioned above, those bits exist in earlier IP as well. Typically non-implemented bits in Allwinner IP as RES0, so I think we can program them unconditionally (and should on H6/A133/H616) and don't need a quirks flag. > + * @sample_div_sel: value for the SPLCFG sample-clock divider field (0 on > + * legacy SoCs that lack the field). Same here: those bits exist back to the A20, even. And their meaning didn't change, if I see this correctly. So no quirk needed, instead we should program them explicitly to the value we want (probably 0). > + * @sample_divisor: module-clock divisor that yields the sample clock; matches > + * @sample_div_sel on newer IP, or the fixed /64 on legacy SoCs. That looks odd: why do we have that value in the first place? Following the things I mention above, the divisor shouldn't be different on the A523. And also, I think we should just do the math in the driver, and calculate the divisor, based on some timing requirement. Which could be something like: aim for a clock period of 8us. Though all the parameters seem to be stable: the 24 MHz OSC input, the dividers in the mod clock, and the post dividers in register 0x34. So there wouldn't be much of a calculation, really. But I still think the driver can figure this out itself, and doesn't need explicit telling of a divisor. So I think we would need a separate patch to fix up driver operation before A523. Then the A523 bits should go on top of this. And maybe make this two patches, one for the edge sample bits, one for the clock calculation. Cheers, Andre > */ > struct sunxi_ir_quirks { > bool has_reset; > int fifo_size; > + bool both_pulse; > + u8 sample_div_sel; > + u32 sample_divisor; > }; > > struct sunxi_ir { > @@ -92,6 +114,9 @@ struct sunxi_ir { > void __iomem *base; > int irq; > int fifo_size; > + bool both_pulse; > + u8 sample_div_sel; > + u32 sample_divisor; > struct clk *clk; > struct clk *apb_clk; > struct reset_control *rst; > @@ -140,17 +165,19 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) > } > > /* Convert idle threshold to usec */ > -static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int ithr) > +static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int div, > + unsigned int ithr) > { > return DIV_ROUND_CLOSEST(USEC_PER_SEC * (ithr + 1), > - base_clk / (128 * 64)); > + base_clk / (128 * div)); > } > > /* Convert usec to idle threshold */ > -static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int usec) > +static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int div, > + unsigned int usec) > { > /* make sure we don't end up with a timeout less than requested */ > - return DIV_ROUND_UP((base_clk / (128 * 64)) * usec, USEC_PER_SEC) - 1; > + return DIV_ROUND_UP((base_clk / (128 * div)) * usec, USEC_PER_SEC) - 1; > } > > static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) > @@ -158,15 +185,17 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) > struct sunxi_ir *ir = rc_dev->priv; > unsigned int base_clk = clk_get_rate(ir->clk); > > - unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout); > + unsigned int ithr = sunxi_usec_to_ithr(base_clk, ir->sample_divisor, > + timeout); > > dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr); > > - /* Set noise threshold and idle threshold */ > - writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), > + /* Set sample clock divider, noise threshold and idle threshold */ > + writel(REG_CIR_SDIV(ir->sample_div_sel) | > + REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), > ir->base + SUNXI_IR_CIR_REG); > > - rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr); > + rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ir->sample_divisor, ithr); > > return 0; > } > @@ -193,8 +222,14 @@ static int sunxi_ir_hw_init(struct device *dev) > goto exit_disable_apb_clk; > } > > - /* Enable CIR Mode */ > - writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG); > + /* > + * Enable CIR Mode. On newer IP revisions the pulse-mode field must > + * also be set, otherwise no pulses are captured into the RX FIFO. > + */ > + tmp = REG_CTL_MD; > + if (ir->both_pulse) > + tmp |= REG_CTL_PMD_BOTH; > + writel(tmp, ir->base + SUNXI_IR_CTL_REG); > > /* Set noise threshold and idle threshold */ > sunxi_ir_set_timeout(ir->rc, ir->rc->timeout); > @@ -271,6 +306,9 @@ static int sunxi_ir_probe(struct platform_device *pdev) > } > > ir->fifo_size = quirks->fifo_size; > + ir->both_pulse = quirks->both_pulse; > + ir->sample_div_sel = quirks->sample_div_sel; > + ir->sample_divisor = quirks->sample_divisor ?: SUNXI_IR_SAMPLE_DIV; > > /* Clock */ > ir->apb_clk = devm_clk_get(dev, "apb"); > @@ -325,10 +363,10 @@ static int sunxi_ir_probe(struct platform_device *pdev) > ir->rc->dev.parent = dev; > ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; > /* Frequency after IR internal divider with sample period in us */ > - ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); > + ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / ir->sample_divisor)); > ir->rc->timeout = IR_DEFAULT_TIMEOUT; > - ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0); > - ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255); > + ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 0); > + ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 255); > ir->rc->s_timeout = sunxi_ir_set_timeout; > ir->rc->driver_name = SUNXI_IR_DEV; > > @@ -395,6 +433,14 @@ static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = { > .fifo_size = 64, > }; > > +static const struct sunxi_ir_quirks sun55i_a523_ir_quirks = { > + .has_reset = true, > + .fifo_size = 64, > + .both_pulse = true, > + .sample_div_sel = 2, /* sample clock = module clock / 256 */ > + .sample_divisor = 256, > +}; > + > static const struct of_device_id sunxi_ir_match[] = { > { > .compatible = "allwinner,sun4i-a10-ir", > @@ -408,6 +454,10 @@ static const struct of_device_id sunxi_ir_match[] = { > .compatible = "allwinner,sun6i-a31-ir", > .data = &sun6i_a31_ir_quirks, > }, > + { > + .compatible = "allwinner,sun55i-a523-ir", > + .data = &sun55i_a523_ir_quirks, > + }, > {} > }; > MODULE_DEVICE_TABLE(of, sunxi_ir_match);
On Fri, Jul 03, 2026 at 11:11:59AM +0200, Andre Przywara wrote: > On Thu, 2 Jul 2026 17:47:48 -0400 > Justin Suess <utilityemal77@gmail.com> wrote: > > Hi Justin, > > many thanks for sending this! > > > The A523 (sun55i) has a newer revision of the CIR receiver IP. Two > > register fields that do not exist on older SoCs must be programmed > > for reception to work: > > > > - CTL bits [7:6] select which pulse polarities are captured into the > > RX FIFO. The reset value of 0 captures nothing, so program "both > > pulse" mode, which captures regardless of header polarity. > > Are you sure about that? The manual says that *both* the 0b00 (reset > default) and 0b01 values capture both edges, and actually the H6, A133 > and H616 have the same bits, and it apparently works there. > No, actually. I literally just live dumped the registers on the vendor kernel, (my android tv came pre-rooted for some reason) and interpreted the purpose from the BSP source. I am actually unaware there was such a manual. If you could point me to it that'd be great. > I don't see those bits documented in the A64 (and earlier), but haven't > checked yet whether they exist there regardless or have an effect. > So I think we should force those bits either to 0 or to 1, depending on how > those bits behave on A64 and before, and how compatible this is with H6, > A133, H616. I will try to run some experiments on the weekend. > Appreciate it! I'll wait for the results of that experiment, I don't feel confident enough that I wouldn't break things by even forcing the register to zero without hardware to test on. So I preserved the existing behavior for the non X98qpro+ board to be safe. > > - SPLCFG (the sample configuration register) bits [1:0] select the > > sample clock as a division of the module clock, replacing the > > fixed module clock / 64 sample rate of the older IP. > > That's not fully correct: even the A20(!) has these two bits, actually > there is a third bit, held in bit 24 (because reasons). All those bits > reset to 0, which is encoded as /64, so this is where the rate comes > from. And sunxi_ir_probe() sets the IR clock to 8MHz, which should end > up as 24MHz / 3, on all chips, including the A523. > > So what is going on here? Is the manual wrong, about those bits, or the > clock sources? > Can you point to the BSP sources, if you used those? > Actually I relied on a script running sunxi-dump on the vendor kernel and dumping the registers for an initial implementation. It didn't work, and I was getting stuck. (probably my sampling script didn't dump the registers fast enough and didn't include all of the ones needed). Then I found https://github.com/chainsx/linux-sun55iw3-vendor And saw this #define IR_SAMPLE_DEV (0x2 << 0) /* 24MHz/256 =93750Hz (~10.7us)*/ for the clock rate. And then this one: #define IR_BOTH_PULSE (0x1 << 6) As and saw it being used as the pulse polarity. (above are in bsp/drivers/ir-rx/sunxi-ir-rx.c) And figured to give this one a try because everything I tried before messing with the existing constants in mainline failed, and this was the primary construct that didn't match up with mainline. Those two things were enough to get it working perfectly on my hardware. So this is more based on "this is what worked on the hardware" + a cursory read of the vendor source than based on any hardware technical document or manual... I should have been more upfront about that. So please point me to this manual and I can revise and figure out WHY this works and improve this patch series. > > module clock / 256, which together with the 24 MHz module clock > > Why is the A523 mod clock set to 24 MHz? You seem to do this in the DT, > overriding the 8MHz default? The driver clearly has a clk_set_rate() call > with that default 8MHz as an argument, and I don't think we should deviate > from that, unless there are good reasons. The sample clock should be more of > a driver/subsystem decision, not a a device one. > See above. This is just what worked on the hardware, and what the vendor set. And it worked so I sent it. I didn't have access to the manual... but agreed this needs justification and not just a handwaving "works on my machine". > > used on the A523 gives a 10.7 μs sample period, close to the 8 μs > > of the previous 8 MHz / 64 configuration, and keeps the default > > 125 ms idle timeout representable in the 8-bit idle threshold > > This is some good info that helps people understand the reasoning behind > those timing values. Please put this in a comment near the top of the file. > But actually: how does this compute? With an 8us sample clock period, the > 8-bit ATHR field only covers 2 ms. And I don't see us setting the ATHC bit > to bump this by 128. > I'll include it and anything else I can glean from your testing, and if you can point me to the manual. Thanks for the swift review, sorry should have made more obvious up front this was a "tweaking until it works" implementation, not a ground up spec based implementation. > Cheers, > Andre > > > field. > > > > Parameterize the sample divisor in the resolution/timeout > > calculations, which older SoCs keep at the fixed 64, and add the > > A523 quirks and compatible. > > > > Signed-off-by: Justin Suess <utilityemal77@gmail.com> > > --- > > drivers/media/rc/sunxi-cir.c | 76 ++++++++++++++++++++++++++++++------ > > 1 file changed, 63 insertions(+), 13 deletions(-) > > > > diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c > > index cb4c56bf0752..82ada9dc0347 100644 > > --- a/drivers/media/rc/sunxi-cir.c > > +++ b/drivers/media/rc/sunxi-cir.c > > @@ -31,6 +31,11 @@ > > /* CIR mode */ > > #define REG_CTL_MD (BIT(4) | BIT(5)) > > +/* Pulse mode selector (bits [7:6]) */ > > +#define REG_CTL_PMD(m) ((m) << 6) > > +/* Capture both pulse polarities */ > > +#define REG_CTL_PMD_BOTH REG_CTL_PMD(1) > > + > > /* Rx Config */ > > #define SUNXI_IR_RXCTL_REG 0x10 > > /* Pulse Polarity Invert flag */ > > @@ -66,6 +71,13 @@ > > /* IR Sample Config */ > > #define SUNXI_IR_CIR_REG 0x34 > > +/* > > + * Sample clock divider select (bits [1:0]), present on newer IP revisions > > + * (e.g. sun55i). Selects the sample clock as a fraction of the module clock; > > + * must be programmed for the sampler to run. Older SoCs lack the field and > > + * use a fixed module-clock/64 sample rate, so they leave it 0. > > + */ > > +#define REG_CIR_SDIV(val) ((val) & GENMASK(1, 0)) > > /* CIR_REG register noise threshold */ > > #define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2))) > > /* CIR_REG register idle threshold */ > > @@ -73,6 +85,8 @@ > > /* Required frequency for IR0 or IR1 clock in CIR mode (default) */ > > #define SUNXI_IR_BASE_CLK 8000000 > > +/* Default sample clock divisor: module clock / 64 (legacy fixed rate) */ > > +#define SUNXI_IR_SAMPLE_DIV 64 > > /* Noise threshold in samples */ > > #define SUNXI_IR_RXNOISE 1 > > @@ -81,10 +95,18 @@ > > * > > * @has_reset: SoC needs reset deasserted. > > * @fifo_size: size of the fifo. > > + * @both_pulse: program the CTRL pulse-mode field (newer IP revisions). > > As mentioned above, those bits exist in earlier IP as well. Typically > non-implemented bits in Allwinner IP as RES0, so I think we can program them > unconditionally (and should on H6/A133/H616) and don't need a quirks flag. > Yeah, probably just setting them to zero is *fine* for the non-a523 hardware, but I didn't want to send patches for hardware I can't test. Last thing I want to do is make someones TV remote stop working. If your experiments reveal forcing zero is OK, than I'll default to that. > > + * @sample_div_sel: value for the SPLCFG sample-clock divider field (0 on > > + * legacy SoCs that lack the field). > > Same here: those bits exist back to the A20, even. And their meaning didn't > change, if I see this correctly. So no quirk needed, instead we should > program them explicitly to the value we want (probably 0). > Sounds good > > + * @sample_divisor: module-clock divisor that yields the sample clock; matches > > + * @sample_div_sel on newer IP, or the fixed /64 on legacy SoCs. > > That looks odd: why do we have that value in the first place? Following the > things I mention above, the divisor shouldn't be different on the A523. And > also, I think we should just do the math in the driver, and calculate the > divisor, based on some timing requirement. Which could be something like: > aim for a clock period of 8us. Though all the parameters seem to be stable: > the 24 MHz OSC input, the dividers in the mod clock, and the post dividers > in register 0x34. So there wouldn't be much of a calculation, really. But I > still think the driver can figure this out itself, and doesn't need explicit > telling of a divisor. > This is just what worked for me; and what the vendor did. But you're right, we shouldn't trust it if the quality of the bsp code is anything to go off of. > So I think we would need a separate patch to fix up driver operation before > A523. Then the A523 bits should go on top of this. And maybe make this two > patches, one for the edge sample bits, one for the clock calculation. > Easy enough. I'll wait on your experiments, please send whatever you found here while I prepare the seperate patches, and I'll adjust before resending (no rush on the results, all I'd need is you forcing the register to zero and verifying it works) (and please if you could drop me this manual, would have saved me a lot of time! I couldn't find it anywhere!) Justin > Cheers, > Andre > > > */ > > struct sunxi_ir_quirks { > > bool has_reset; > > int fifo_size; > > + bool both_pulse; > > + u8 sample_div_sel; > > + u32 sample_divisor; > > }; > > struct sunxi_ir { > > @@ -92,6 +114,9 @@ struct sunxi_ir { > > void __iomem *base; > > int irq; > > int fifo_size; > > + bool both_pulse; > > + u8 sample_div_sel; > > + u32 sample_divisor; > > struct clk *clk; > > struct clk *apb_clk; > > struct reset_control *rst; > > @@ -140,17 +165,19 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) > > } > > /* Convert idle threshold to usec */ > > -static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int ithr) > > +static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int div, > > + unsigned int ithr) > > { > > return DIV_ROUND_CLOSEST(USEC_PER_SEC * (ithr + 1), > > - base_clk / (128 * 64)); > > + base_clk / (128 * div)); > > } > > /* Convert usec to idle threshold */ > > -static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int usec) > > +static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int div, > > + unsigned int usec) > > { > > /* make sure we don't end up with a timeout less than requested */ > > - return DIV_ROUND_UP((base_clk / (128 * 64)) * usec, USEC_PER_SEC) - 1; > > + return DIV_ROUND_UP((base_clk / (128 * div)) * usec, USEC_PER_SEC) - 1; > > } > > static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) > > @@ -158,15 +185,17 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) > > struct sunxi_ir *ir = rc_dev->priv; > > unsigned int base_clk = clk_get_rate(ir->clk); > > - unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout); > > + unsigned int ithr = sunxi_usec_to_ithr(base_clk, ir->sample_divisor, > > + timeout); > > dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr); > > - /* Set noise threshold and idle threshold */ > > - writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), > > + /* Set sample clock divider, noise threshold and idle threshold */ > > + writel(REG_CIR_SDIV(ir->sample_div_sel) | > > + REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), > > ir->base + SUNXI_IR_CIR_REG); > > - rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr); > > + rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ir->sample_divisor, ithr); > > return 0; > > } > > @@ -193,8 +222,14 @@ static int sunxi_ir_hw_init(struct device *dev) > > goto exit_disable_apb_clk; > > } > > - /* Enable CIR Mode */ > > - writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG); > > + /* > > + * Enable CIR Mode. On newer IP revisions the pulse-mode field must > > + * also be set, otherwise no pulses are captured into the RX FIFO. > > + */ > > + tmp = REG_CTL_MD; > > + if (ir->both_pulse) > > + tmp |= REG_CTL_PMD_BOTH; > > + writel(tmp, ir->base + SUNXI_IR_CTL_REG); > > /* Set noise threshold and idle threshold */ > > sunxi_ir_set_timeout(ir->rc, ir->rc->timeout); > > @@ -271,6 +306,9 @@ static int sunxi_ir_probe(struct platform_device *pdev) > > } > > ir->fifo_size = quirks->fifo_size; > > + ir->both_pulse = quirks->both_pulse; > > + ir->sample_div_sel = quirks->sample_div_sel; > > + ir->sample_divisor = quirks->sample_divisor ?: SUNXI_IR_SAMPLE_DIV; > > /* Clock */ > > ir->apb_clk = devm_clk_get(dev, "apb"); > > @@ -325,10 +363,10 @@ static int sunxi_ir_probe(struct platform_device *pdev) > > ir->rc->dev.parent = dev; > > ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; > > /* Frequency after IR internal divider with sample period in us */ > > - ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); > > + ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / ir->sample_divisor)); > > ir->rc->timeout = IR_DEFAULT_TIMEOUT; > > - ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0); > > - ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255); > > + ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 0); > > + ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 255); > > ir->rc->s_timeout = sunxi_ir_set_timeout; > > ir->rc->driver_name = SUNXI_IR_DEV; > > @@ -395,6 +433,14 @@ static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = { > > .fifo_size = 64, > > }; > > +static const struct sunxi_ir_quirks sun55i_a523_ir_quirks = { > > + .has_reset = true, > > + .fifo_size = 64, > > + .both_pulse = true, > > + .sample_div_sel = 2, /* sample clock = module clock / 256 */ > > + .sample_divisor = 256, > > +}; > > + > > static const struct of_device_id sunxi_ir_match[] = { > > { > > .compatible = "allwinner,sun4i-a10-ir", > > @@ -408,6 +454,10 @@ static const struct of_device_id sunxi_ir_match[] = { > > .compatible = "allwinner,sun6i-a31-ir", > > .data = &sun6i_a31_ir_quirks, > > }, > > + { > > + .compatible = "allwinner,sun55i-a523-ir", > > + .data = &sun55i_a523_ir_quirks, > > + }, > > {} > > }; > > MODULE_DEVICE_TABLE(of, sunxi_ir_match); >
Hi Justin, On 7/3/26 21:47, Justin Suess wrote: > On Fri, Jul 03, 2026 at 11:11:59AM +0200, Andre Przywara wrote: >> On Thu, 2 Jul 2026 17:47:48 -0400 >> Justin Suess <utilityemal77@gmail.com> wrote: >> >> Hi Justin, >> >> many thanks for sending this! >> >>> The A523 (sun55i) has a newer revision of the CIR receiver IP. Two >>> register fields that do not exist on older SoCs must be programmed >>> for reception to work: >>> >>> - CTL bits [7:6] select which pulse polarities are captured into the >>> RX FIFO. The reset value of 0 captures nothing, so program "both >>> pulse" mode, which captures regardless of header polarity. >> >> Are you sure about that? The manual says that *both* the 0b00 (reset >> default) and 0b01 values capture both edges, and actually the H6, A133 >> and H616 have the same bits, and it apparently works there. >> > No, actually. I literally just live dumped the registers on the vendor > kernel, (my android tv came pre-rooted for some reason) and interpreted > the purpose from the BSP source. > > I am actually unaware there was such a manual. If you could point me to > it that'd be great. >> I don't see those bits documented in the A64 (and earlier), but haven't >> checked yet whether they exist there regardless or have an effect. >> So I think we should force those bits either to 0 or to 1, depending on how >> those bits behave on A64 and before, and how compatible this is with H6, >> A133, H616. I will try to run some experiments on the weekend. >> > Appreciate it! > > I'll wait for the results of that experiment, I don't feel confident > enough that I wouldn't break things by even forcing the register to zero > without hardware to test on. So I preserved the existing behavior for the non > X98qpro+ board to be safe. So I tested IR on an H5 and H616 board, both are happy with those bits being either 0b00 or 0b01. Setting bit 7 however broke things, I sometimes got error messages that indicate it missed a pulse, so bit 7 selecting one of the single-pulse-only settings would make sense. Even on the H5, where this isn't documented in the manual, but behaves the same as in the later chips. AND I also tested on the X96QPro+, and it worked there without any driver changes: just with the A31 fallback compatible! Can you confirm this? This would render this whole patch here moot: - We don't need a special "both pulse" setting, as the 0b00 setting already selects that, and we force those bits to 0 already by initialising the variable to REG_CTL_MD, which just sets bits [5:4]. - As mentioned, I think setting the clock-frequency property in the DT is misleading: we don't need to do that, and then it just works. I actually question the usefulness of this property at all: first it shouldn't be the CIR input clock frequency, but the sample frequency (so 125 KHz, for instance). And secondly I wonder if the DT is the right place to set this frequency in the first place, if anything, I would expect the specific IR protocol to have some minimum sample rate requirement. So I wonder if we should deprecate this property in the DT bindings altogether. >>> - SPLCFG (the sample configuration register) bits [1:0] select the >>> sample clock as a division of the module clock, replacing the >>> fixed module clock / 64 sample rate of the older IP. >> >> That's not fully correct: even the A20(!) has these two bits, actually >> there is a third bit, held in bit 24 (because reasons). All those bits >> reset to 0, which is encoded as /64, so this is where the rate comes >> from. And sunxi_ir_probe() sets the IR clock to 8MHz, which should end >> up as 24MHz / 3, on all chips, including the A523. >> >> So what is going on here? Is the manual wrong, about those bits, or the >> clock sources? >> Can you point to the BSP sources, if you used those? >> > > Actually I relied on a script running sunxi-dump on the vendor > kernel and dumping the registers for an initial implementation. > > It didn't work, and I was getting stuck. (probably my sampling script > didn't dump the registers fast enough and didn't include all of the ones > needed). > > Then I found https://github.com/chainsx/linux-sun55iw3-vendor > > And saw this > > #define IR_SAMPLE_DEV (0x2 << 0) /* 24MHz/256 =93750Hz (~10.7us)*/ > > for the clock rate. > > And then this one: > > #define IR_BOTH_PULSE (0x1 << 6) > > As and saw it being used as the pulse polarity. > > (above are in bsp/drivers/ir-rx/sunxi-ir-rx.c) > > And figured to give this one a try because everything I tried before > messing with the existing constants in mainline failed, and this was > the primary construct that didn't match up with mainline. > > Those two things were enough to get it working perfectly on my hardware. Interesting, so can you double check that it works for you without any driver changes? Or revert your changes one-by-one, so selecting the default 8 MHz clock, while keeping the /64 divider, then using (0x0 << 6) for the pulse detection bits. Because this is what effectively worked for me. > So this is more based on "this is what worked on the hardware" + a > cursory read of the vendor source than based on any hardware technical > document or manual... I should have been more upfront about that. That's fine, if you confirm things by experiements, that's a proper approach, as the BSP code contains quite some nonsense at times. What I typically add to my experiments to be more confident is trying to break things again: so set a bit which you think should be clear, and see if it stops working. You can even mess around with the register bits on a live system, by using my peekpoke tool[1], for instance. To dump SUNXI_IR_CIR_REG, then set bit 7 in SUNXI_IR_CTL_REG: # peekpoke -b 0x07040000 r.l 0x34 s.l 0x0 7 [1] https://github.com/apritzel/peekpoke > So please point me to this manual and I can revise and figure out WHY > this works and improve this patch series. Just for the records (as responded on IRC), we keep links to the manuals in our wiki: https://linux-sunxi.org/A523#Documentation Those are just the early A523 manuals, though, there are more floating around: https://gitlab.com/tina5.0_aiot/product/docs/-/tree/product-aiot-stable?ref_type=heads Cheers, Andre >>> module clock / 256, which together with the 24 MHz module clock >> >> Why is the A523 mod clock set to 24 MHz? You seem to do this in the DT, >> overriding the 8MHz default? The driver clearly has a clk_set_rate() call >> with that default 8MHz as an argument, and I don't think we should deviate >> from that, unless there are good reasons. The sample clock should be more of >> a driver/subsystem decision, not a a device one. >> > > See above. This is just what worked on the hardware, and what the vendor > set. And it worked so I sent it. I didn't have access to the manual... > > but agreed this needs justification and not just a handwaving "works on > my machine". > >>> used on the A523 gives a 10.7 μs sample period, close to the 8 μs >>> of the previous 8 MHz / 64 configuration, and keeps the default >>> 125 ms idle timeout representable in the 8-bit idle threshold >> >> This is some good info that helps people understand the reasoning behind >> those timing values. Please put this in a comment near the top of the file. >> But actually: how does this compute? With an 8us sample clock period, the >> 8-bit ATHR field only covers 2 ms. And I don't see us setting the ATHC bit >> to bump this by 128. >> > I'll include it and anything else I can glean from your testing, and if > you can point me to the manual. > > Thanks for the swift review, sorry should have made more obvious up > front this was a "tweaking until it works" implementation, not a ground > up spec based implementation. > >> Cheers, >> Andre >> >>> field. >>> >>> Parameterize the sample divisor in the resolution/timeout >>> calculations, which older SoCs keep at the fixed 64, and add the >>> A523 quirks and compatible. >>> >>> Signed-off-by: Justin Suess <utilityemal77@gmail.com> >>> --- >>> drivers/media/rc/sunxi-cir.c | 76 ++++++++++++++++++++++++++++++------ >>> 1 file changed, 63 insertions(+), 13 deletions(-) >>> >>> diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c >>> index cb4c56bf0752..82ada9dc0347 100644 >>> --- a/drivers/media/rc/sunxi-cir.c >>> +++ b/drivers/media/rc/sunxi-cir.c >>> @@ -31,6 +31,11 @@ >>> /* CIR mode */ >>> #define REG_CTL_MD (BIT(4) | BIT(5)) >>> +/* Pulse mode selector (bits [7:6]) */ >>> +#define REG_CTL_PMD(m) ((m) << 6) >>> +/* Capture both pulse polarities */ >>> +#define REG_CTL_PMD_BOTH REG_CTL_PMD(1) >>> + >>> /* Rx Config */ >>> #define SUNXI_IR_RXCTL_REG 0x10 >>> /* Pulse Polarity Invert flag */ >>> @@ -66,6 +71,13 @@ >>> /* IR Sample Config */ >>> #define SUNXI_IR_CIR_REG 0x34 >>> +/* >>> + * Sample clock divider select (bits [1:0]), present on newer IP revisions >>> + * (e.g. sun55i). Selects the sample clock as a fraction of the module clock; >>> + * must be programmed for the sampler to run. Older SoCs lack the field and >>> + * use a fixed module-clock/64 sample rate, so they leave it 0. >>> + */ >>> +#define REG_CIR_SDIV(val) ((val) & GENMASK(1, 0)) >>> /* CIR_REG register noise threshold */ >>> #define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2))) >>> /* CIR_REG register idle threshold */ >>> @@ -73,6 +85,8 @@ >>> /* Required frequency for IR0 or IR1 clock in CIR mode (default) */ >>> #define SUNXI_IR_BASE_CLK 8000000 >>> +/* Default sample clock divisor: module clock / 64 (legacy fixed rate) */ >>> +#define SUNXI_IR_SAMPLE_DIV 64 >>> /* Noise threshold in samples */ >>> #define SUNXI_IR_RXNOISE 1 >>> @@ -81,10 +95,18 @@ >>> * >>> * @has_reset: SoC needs reset deasserted. >>> * @fifo_size: size of the fifo. >>> + * @both_pulse: program the CTRL pulse-mode field (newer IP revisions). >> >> As mentioned above, those bits exist in earlier IP as well. Typically >> non-implemented bits in Allwinner IP as RES0, so I think we can program them >> unconditionally (and should on H6/A133/H616) and don't need a quirks flag. >> > Yeah, probably just setting them to zero is *fine* for the non-a523 > hardware, but I didn't want to send patches for hardware I can't test. > > Last thing I want to do is make someones TV remote stop working. > > If your experiments reveal forcing zero is OK, than I'll default to > that. > >>> + * @sample_div_sel: value for the SPLCFG sample-clock divider field (0 on >>> + * legacy SoCs that lack the field). >> >> Same here: those bits exist back to the A20, even. And their meaning didn't >> change, if I see this correctly. So no quirk needed, instead we should >> program them explicitly to the value we want (probably 0). >> > Sounds good >>> + * @sample_divisor: module-clock divisor that yields the sample clock; matches >>> + * @sample_div_sel on newer IP, or the fixed /64 on legacy SoCs. >> >> That looks odd: why do we have that value in the first place? Following the >> things I mention above, the divisor shouldn't be different on the A523. And >> also, I think we should just do the math in the driver, and calculate the >> divisor, based on some timing requirement. Which could be something like: >> aim for a clock period of 8us. Though all the parameters seem to be stable: >> the 24 MHz OSC input, the dividers in the mod clock, and the post dividers >> in register 0x34. So there wouldn't be much of a calculation, really. But I >> still think the driver can figure this out itself, and doesn't need explicit >> telling of a divisor. >> > This is just what worked for me; and what the vendor did. But you're > right, we shouldn't trust it if the quality of the bsp code is anything > to go off of. > >> So I think we would need a separate patch to fix up driver operation before >> A523. Then the A523 bits should go on top of this. And maybe make this two >> patches, one for the edge sample bits, one for the clock calculation. >> > Easy enough. I'll wait on your experiments, please send whatever you > found here while I prepare the seperate patches, and I'll adjust before > resending (no rush on the results, all I'd need is you forcing the > register to zero and verifying it works) > > (and please if you could drop me this manual, would have saved me a lot of time! > I couldn't find it anywhere!) > > Justin >> Cheers, >> Andre >> >>> */ >>> struct sunxi_ir_quirks { >>> bool has_reset; >>> int fifo_size; >>> + bool both_pulse; >>> + u8 sample_div_sel; >>> + u32 sample_divisor; >>> }; >>> struct sunxi_ir { >>> @@ -92,6 +114,9 @@ struct sunxi_ir { >>> void __iomem *base; >>> int irq; >>> int fifo_size; >>> + bool both_pulse; >>> + u8 sample_div_sel; >>> + u32 sample_divisor; >>> struct clk *clk; >>> struct clk *apb_clk; >>> struct reset_control *rst; >>> @@ -140,17 +165,19 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) >>> } >>> /* Convert idle threshold to usec */ >>> -static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int ithr) >>> +static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int div, >>> + unsigned int ithr) >>> { >>> return DIV_ROUND_CLOSEST(USEC_PER_SEC * (ithr + 1), >>> - base_clk / (128 * 64)); >>> + base_clk / (128 * div)); >>> } >>> /* Convert usec to idle threshold */ >>> -static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int usec) >>> +static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int div, >>> + unsigned int usec) >>> { >>> /* make sure we don't end up with a timeout less than requested */ >>> - return DIV_ROUND_UP((base_clk / (128 * 64)) * usec, USEC_PER_SEC) - 1; >>> + return DIV_ROUND_UP((base_clk / (128 * div)) * usec, USEC_PER_SEC) - 1; >>> } >>> static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) >>> @@ -158,15 +185,17 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) >>> struct sunxi_ir *ir = rc_dev->priv; >>> unsigned int base_clk = clk_get_rate(ir->clk); >>> - unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout); >>> + unsigned int ithr = sunxi_usec_to_ithr(base_clk, ir->sample_divisor, >>> + timeout); >>> dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr); >>> - /* Set noise threshold and idle threshold */ >>> - writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), >>> + /* Set sample clock divider, noise threshold and idle threshold */ >>> + writel(REG_CIR_SDIV(ir->sample_div_sel) | >>> + REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), >>> ir->base + SUNXI_IR_CIR_REG); >>> - rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr); >>> + rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ir->sample_divisor, ithr); >>> return 0; >>> } >>> @@ -193,8 +222,14 @@ static int sunxi_ir_hw_init(struct device *dev) >>> goto exit_disable_apb_clk; >>> } >>> - /* Enable CIR Mode */ >>> - writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG); >>> + /* >>> + * Enable CIR Mode. On newer IP revisions the pulse-mode field must >>> + * also be set, otherwise no pulses are captured into the RX FIFO. >>> + */ >>> + tmp = REG_CTL_MD; >>> + if (ir->both_pulse) >>> + tmp |= REG_CTL_PMD_BOTH; >>> + writel(tmp, ir->base + SUNXI_IR_CTL_REG); >>> /* Set noise threshold and idle threshold */ >>> sunxi_ir_set_timeout(ir->rc, ir->rc->timeout); >>> @@ -271,6 +306,9 @@ static int sunxi_ir_probe(struct platform_device *pdev) >>> } >>> ir->fifo_size = quirks->fifo_size; >>> + ir->both_pulse = quirks->both_pulse; >>> + ir->sample_div_sel = quirks->sample_div_sel; >>> + ir->sample_divisor = quirks->sample_divisor ?: SUNXI_IR_SAMPLE_DIV; >>> /* Clock */ >>> ir->apb_clk = devm_clk_get(dev, "apb"); >>> @@ -325,10 +363,10 @@ static int sunxi_ir_probe(struct platform_device *pdev) >>> ir->rc->dev.parent = dev; >>> ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; >>> /* Frequency after IR internal divider with sample period in us */ >>> - ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); >>> + ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / ir->sample_divisor)); >>> ir->rc->timeout = IR_DEFAULT_TIMEOUT; >>> - ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0); >>> - ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255); >>> + ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 0); >>> + ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 255); >>> ir->rc->s_timeout = sunxi_ir_set_timeout; >>> ir->rc->driver_name = SUNXI_IR_DEV; >>> @@ -395,6 +433,14 @@ static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = { >>> .fifo_size = 64, >>> }; >>> +static const struct sunxi_ir_quirks sun55i_a523_ir_quirks = { >>> + .has_reset = true, >>> + .fifo_size = 64, >>> + .both_pulse = true, >>> + .sample_div_sel = 2, /* sample clock = module clock / 256 */ >>> + .sample_divisor = 256, >>> +}; >>> + >>> static const struct of_device_id sunxi_ir_match[] = { >>> { >>> .compatible = "allwinner,sun4i-a10-ir", >>> @@ -408,6 +454,10 @@ static const struct of_device_id sunxi_ir_match[] = { >>> .compatible = "allwinner,sun6i-a31-ir", >>> .data = &sun6i_a31_ir_quirks, >>> }, >>> + { >>> + .compatible = "allwinner,sun55i-a523-ir", >>> + .data = &sun55i_a523_ir_quirks, >>> + }, >>> {} >>> }; >>> MODULE_DEVICE_TABLE(of, sunxi_ir_match); >>
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index cb4c56bf0752..82ada9dc0347 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -31,6 +31,11 @@ /* CIR mode */ #define REG_CTL_MD (BIT(4) | BIT(5)) +/* Pulse mode selector (bits [7:6]) */ +#define REG_CTL_PMD(m) ((m) << 6) +/* Capture both pulse polarities */ +#define REG_CTL_PMD_BOTH REG_CTL_PMD(1) + /* Rx Config */ #define SUNXI_IR_RXCTL_REG 0x10 /* Pulse Polarity Invert flag */ @@ -66,6 +71,13 @@ /* IR Sample Config */ #define SUNXI_IR_CIR_REG 0x34 +/* + * Sample clock divider select (bits [1:0]), present on newer IP revisions + * (e.g. sun55i). Selects the sample clock as a fraction of the module clock; + * must be programmed for the sampler to run. Older SoCs lack the field and + * use a fixed module-clock/64 sample rate, so they leave it 0. + */ +#define REG_CIR_SDIV(val) ((val) & GENMASK(1, 0)) /* CIR_REG register noise threshold */ #define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2))) /* CIR_REG register idle threshold */ @@ -73,6 +85,8 @@ /* Required frequency for IR0 or IR1 clock in CIR mode (default) */ #define SUNXI_IR_BASE_CLK 8000000 +/* Default sample clock divisor: module clock / 64 (legacy fixed rate) */ +#define SUNXI_IR_SAMPLE_DIV 64 /* Noise threshold in samples */ #define SUNXI_IR_RXNOISE 1 @@ -81,10 +95,18 @@ * * @has_reset: SoC needs reset deasserted. * @fifo_size: size of the fifo. + * @both_pulse: program the CTRL pulse-mode field (newer IP revisions). + * @sample_div_sel: value for the SPLCFG sample-clock divider field (0 on + * legacy SoCs that lack the field). + * @sample_divisor: module-clock divisor that yields the sample clock; matches + * @sample_div_sel on newer IP, or the fixed /64 on legacy SoCs. */ struct sunxi_ir_quirks { bool has_reset; int fifo_size; + bool both_pulse; + u8 sample_div_sel; + u32 sample_divisor; }; struct sunxi_ir { @@ -92,6 +114,9 @@ struct sunxi_ir { void __iomem *base; int irq; int fifo_size; + bool both_pulse; + u8 sample_div_sel; + u32 sample_divisor; struct clk *clk; struct clk *apb_clk; struct reset_control *rst; @@ -140,17 +165,19 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) } /* Convert idle threshold to usec */ -static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int ithr) +static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int div, + unsigned int ithr) { return DIV_ROUND_CLOSEST(USEC_PER_SEC * (ithr + 1), - base_clk / (128 * 64)); + base_clk / (128 * div)); } /* Convert usec to idle threshold */ -static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int usec) +static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int div, + unsigned int usec) { /* make sure we don't end up with a timeout less than requested */ - return DIV_ROUND_UP((base_clk / (128 * 64)) * usec, USEC_PER_SEC) - 1; + return DIV_ROUND_UP((base_clk / (128 * div)) * usec, USEC_PER_SEC) - 1; } static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) @@ -158,15 +185,17 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) struct sunxi_ir *ir = rc_dev->priv; unsigned int base_clk = clk_get_rate(ir->clk); - unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout); + unsigned int ithr = sunxi_usec_to_ithr(base_clk, ir->sample_divisor, + timeout); dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr); - /* Set noise threshold and idle threshold */ - writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), + /* Set sample clock divider, noise threshold and idle threshold */ + writel(REG_CIR_SDIV(ir->sample_div_sel) | + REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), ir->base + SUNXI_IR_CIR_REG); - rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr); + rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ir->sample_divisor, ithr); return 0; } @@ -193,8 +222,14 @@ static int sunxi_ir_hw_init(struct device *dev) goto exit_disable_apb_clk; } - /* Enable CIR Mode */ - writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG); + /* + * Enable CIR Mode. On newer IP revisions the pulse-mode field must + * also be set, otherwise no pulses are captured into the RX FIFO. + */ + tmp = REG_CTL_MD; + if (ir->both_pulse) + tmp |= REG_CTL_PMD_BOTH; + writel(tmp, ir->base + SUNXI_IR_CTL_REG); /* Set noise threshold and idle threshold */ sunxi_ir_set_timeout(ir->rc, ir->rc->timeout); @@ -271,6 +306,9 @@ static int sunxi_ir_probe(struct platform_device *pdev) } ir->fifo_size = quirks->fifo_size; + ir->both_pulse = quirks->both_pulse; + ir->sample_div_sel = quirks->sample_div_sel; + ir->sample_divisor = quirks->sample_divisor ?: SUNXI_IR_SAMPLE_DIV; /* Clock */ ir->apb_clk = devm_clk_get(dev, "apb"); @@ -325,10 +363,10 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rc->dev.parent = dev; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; /* Frequency after IR internal divider with sample period in us */ - ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); + ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / ir->sample_divisor)); ir->rc->timeout = IR_DEFAULT_TIMEOUT; - ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0); - ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255); + ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 0); + ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 255); ir->rc->s_timeout = sunxi_ir_set_timeout; ir->rc->driver_name = SUNXI_IR_DEV; @@ -395,6 +433,14 @@ static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = { .fifo_size = 64, }; +static const struct sunxi_ir_quirks sun55i_a523_ir_quirks = { + .has_reset = true, + .fifo_size = 64, + .both_pulse = true, + .sample_div_sel = 2, /* sample clock = module clock / 256 */ + .sample_divisor = 256, +}; + static const struct of_device_id sunxi_ir_match[] = { { .compatible = "allwinner,sun4i-a10-ir", @@ -408,6 +454,10 @@ static const struct of_device_id sunxi_ir_match[] = { .compatible = "allwinner,sun6i-a31-ir", .data = &sun6i_a31_ir_quirks, }, + { + .compatible = "allwinner,sun55i-a523-ir", + .data = &sun55i_a523_ir_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sunxi_ir_match);