* [net-next v8 3/3] net: ethernet: mtk_eth_soc: Add LRO support
From: Frank Wunderlich @ 2026-05-09 19:09 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Russell King
Cc: Frank Wunderlich, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek, Mason Chang, Daniel Golle
In-Reply-To: <20260509190938.169290-1-linux@fw-web.de>
From: Mason Chang <mason-cw.chang@mediatek.com>
Add Large Receive Offload support to mediatek ethernet driver and
activate it for MT7988.
Signed-off-by: Mason Chang <mason-cw.chang@mediatek.com>
Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
---
v8:
- fix u32 vs. be32 completely (with sparse check) except one unrelated one
v7:
- fix u32 vs. be32 reported by patchwork check
- add L4 PSH check
https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/7521c42b0bd5be20d52e20b110daea8c756fc069%5E%21/#F1
- Add HW LRO max 4-depth VLAN support including switch special tag.
https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/35490cec6a2e5982532935fb0a1c884f7c4efdb0%5E%21/#F2
v6:
- fix some Macro argument '...' may be better as '(...)' to avoid precedence issues for LRO
- drop unused MTK_CTRL_DW0_SDL_MASK
v5:
- fix too long lines reported by checkpatch
MTK_LRO_RING_RELINQUISH_REQ
MTK_LRO_RING_RELINQUISH_DONE
irq handling (MTK_HW_LRO_IRQ + MTK_HW_LRO_RING)
v4:
- fix lro reverse christmas tree and LRO params suggested by andrew
- drop mtk_hwlro_stats_ebl and unused IS_HW_LRO_RING (only used in
properitary debugfs)
v2:
- drop link to commit for 6.6 patch
---
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 263 +++++++++++++++++---
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 53 ++--
2 files changed, 254 insertions(+), 62 deletions(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 908fd88287ac..8035fc2557de 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2793,7 +2793,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
if (rx_flag == MTK_RX_FLAGS_HWLRO) {
rx_data_len = MTK_MAX_LRO_RX_LENGTH;
- rx_dma_size = MTK_HW_LRO_DMA_SIZE;
+ rx_dma_size = MTK_HW_LRO_DMA_SIZE(eth);
} else {
rx_data_len = ETH_DATA_LEN;
rx_dma_size = soc->rx.dma_size;
@@ -2806,7 +2806,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
if (!ring->data)
return -ENOMEM;
- if (mtk_page_pool_enabled(eth)) {
+ if (mtk_page_pool_enabled(eth) && rcu_access_pointer(eth->prog)) {
struct page_pool *pp;
pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no,
@@ -2952,10 +2952,11 @@ static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, bool in_
static int mtk_hwlro_rx_init(struct mtk_eth *eth)
{
- const struct mtk_reg_map *reg_map = eth->soc->reg_map;
- int i;
u32 ring_ctrl_dw1 = 0, ring_ctrl_dw2 = 0, ring_ctrl_dw3 = 0;
+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ const struct mtk_soc_data *soc = eth->soc;
u32 lro_ctrl_dw0 = 0, lro_ctrl_dw3 = 0;
+ int i, val;
/* set LRO rings to auto-learn modes */
ring_ctrl_dw2 |= MTK_RING_AUTO_LERAN_MODE;
@@ -2974,30 +2975,50 @@ static int mtk_hwlro_rx_init(struct mtk_eth *eth)
ring_ctrl_dw2 |= MTK_RING_MAX_AGG_CNT_L;
ring_ctrl_dw3 |= MTK_RING_MAX_AGG_CNT_H;
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) {
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM(eth); i++) {
mtk_w32(eth, ring_ctrl_dw1, MTK_LRO_CTRL_DW1_CFG(reg_map, i));
mtk_w32(eth, ring_ctrl_dw2, MTK_LRO_CTRL_DW2_CFG(reg_map, i));
mtk_w32(eth, ring_ctrl_dw3, MTK_LRO_CTRL_DW3_CFG(reg_map, i));
}
/* IPv4 checksum update enable */
- lro_ctrl_dw0 |= MTK_L3_CKS_UPD_EN;
+ lro_ctrl_dw0 |= MTK_L3_CKS_UPD_EN(eth);
/* switch priority comparison to packet count mode */
lro_ctrl_dw0 |= MTK_LRO_ALT_PKT_CNT_MODE;
+ /* enable L4 PSH flag check */
+ lro_ctrl_dw0 |= MTK_LRO_L4_CTRL_PSH_EN;
+
/* bandwidth threshold setting */
- mtk_w32(eth, MTK_HW_LRO_BW_THRE, MTK_PDMA_LRO_CTRL_DW2);
+ mtk_w32(eth, MTK_HW_LRO_BW_THRE, MTK_PDMA_LRO_CTRL_DW2(reg_map));
/* auto-learn score delta setting */
- mtk_w32(eth, MTK_HW_LRO_REPLACE_DELTA, MTK_PDMA_LRO_ALT_SCORE_DELTA);
+ mtk_w32(eth, MTK_HW_LRO_REPLACE_DELTA, MTK_PDMA_LRO_ALT_SCORE_DELTA(reg_map));
/* set refresh timer for altering flows to 1 sec. (unit: 20us) */
mtk_w32(eth, (MTK_HW_LRO_TIMER_UNIT << 16) | MTK_HW_LRO_REFRESH_TIME,
MTK_PDMA_LRO_ALT_REFRESH_TIMER);
- /* set HW LRO mode & the max aggregation count for rx packets */
- lro_ctrl_dw3 |= MTK_ADMA_MODE | (MTK_HW_LRO_MAX_AGG_CNT & 0xff);
+ if (mtk_is_netsys_v3_or_greater(eth)) {
+ val = mtk_r32(eth, reg_map->pdma.rx_cfg);
+ mtk_w32(eth, val | ((MTK_PDMA_LRO_SDL + MTK_MAX_RX_LENGTH) <<
+ MTK_RX_CFG_SDL_OFFSET), reg_map->pdma.rx_cfg);
+
+ lro_ctrl_dw0 |= MTK_PDMA_LRO_SDL << MTK_CTRL_DW0_SDL_OFFSET;
+
+ /* enable cpu reason black list */
+ lro_ctrl_dw0 |= MTK_LRO_CRSN_BNW(eth);
+
+ /* no use PPE cpu reason */
+ mtk_w32(eth, 0xffffffff, MTK_PDMA_LRO_CTRL_DW1(reg_map));
+ } else {
+ /* set HW LRO mode & the max aggregation count for rx packets */
+ lro_ctrl_dw3 |= MTK_ADMA_MODE | (MTK_HW_LRO_MAX_AGG_CNT & 0xff);
+ }
+
+ /* enable max 4-depth VLAN support including switch special tag */
+ lro_ctrl_dw3 |= MTK_LRO_VLAN_VID_CMP_DEPTH | MTK_LRO_VLAN_EN;
/* the minimal remaining room of SDL0 in RXD for lro aggregation */
lro_ctrl_dw3 |= MTK_LRO_MIN_RXD_SDL;
@@ -3005,8 +3026,19 @@ static int mtk_hwlro_rx_init(struct mtk_eth *eth)
/* enable HW LRO */
lro_ctrl_dw0 |= MTK_LRO_EN;
- mtk_w32(eth, lro_ctrl_dw3, MTK_PDMA_LRO_CTRL_DW3);
- mtk_w32(eth, lro_ctrl_dw0, MTK_PDMA_LRO_CTRL_DW0);
+ mtk_w32(eth, lro_ctrl_dw3, MTK_PDMA_LRO_CTRL_DW3(reg_map));
+ mtk_w32(eth, lro_ctrl_dw0, MTK_PDMA_LRO_CTRL_DW0(reg_map));
+
+ if (mtk_is_netsys_v2_or_greater(eth)) {
+ i = (soc->rx.desc_size == sizeof(struct mtk_rx_dma_v2)) ? 1 : 0;
+ mtk_m32(eth, MTK_RX_DONE_INT(eth, MTK_HW_LRO_RING(eth, i)),
+ MTK_RX_DONE_INT(eth, MTK_HW_LRO_RING(eth, i)), reg_map->pdma.int_grp);
+ mtk_m32(eth, MTK_RX_DONE_INT(eth, MTK_HW_LRO_RING(eth, i + 1)),
+ MTK_RX_DONE_INT(eth, MTK_HW_LRO_RING(eth, i + 1)),
+ reg_map->pdma.int_grp + 0x4);
+ mtk_m32(eth, MTK_RX_DONE_INT(eth, MTK_HW_LRO_RING(eth, i + 2)),
+ MTK_RX_DONE_INT(eth, MTK_HW_LRO_RING(eth, i + 2)), reg_map->pdma.int_grp3);
+ }
return 0;
}
@@ -3014,16 +3046,16 @@ static int mtk_hwlro_rx_init(struct mtk_eth *eth)
static void mtk_hwlro_rx_uninit(struct mtk_eth *eth)
{
const struct mtk_reg_map *reg_map = eth->soc->reg_map;
- int i;
u32 val;
+ int i;
/* relinquish lro rings, flush aggregated packets */
- mtk_w32(eth, MTK_LRO_RING_RELINQUISH_REQ, MTK_PDMA_LRO_CTRL_DW0);
+ mtk_w32(eth, MTK_LRO_RING_RELINQUISH_REQ(eth), MTK_PDMA_LRO_CTRL_DW0(reg_map));
/* wait for relinquishments done */
for (i = 0; i < 10; i++) {
- val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW0);
- if (val & MTK_LRO_RING_RELINQUISH_DONE) {
+ val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW0(reg_map));
+ if (val & MTK_LRO_RING_RELINQUISH_DONE(eth)) {
msleep(20);
continue;
}
@@ -3031,14 +3063,14 @@ static void mtk_hwlro_rx_uninit(struct mtk_eth *eth)
}
/* invalidate lro rings */
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++)
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM(eth); i++)
mtk_w32(eth, 0, MTK_LRO_CTRL_DW2_CFG(reg_map, i));
/* disable HW LRO */
- mtk_w32(eth, 0, MTK_PDMA_LRO_CTRL_DW0);
+ mtk_w32(eth, 0, MTK_PDMA_LRO_CTRL_DW0(reg_map));
}
-static void mtk_hwlro_val_ipaddr(struct mtk_eth *eth, int idx, __be32 ip)
+static void mtk_hwlro_val_ipaddr(struct mtk_eth *eth, int idx, u32 ip)
{
const struct mtk_reg_map *reg_map = eth->soc->reg_map;
u32 reg_val;
@@ -3048,7 +3080,7 @@ static void mtk_hwlro_val_ipaddr(struct mtk_eth *eth, int idx, __be32 ip)
/* invalidate the IP setting */
mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(reg_map, idx));
- mtk_w32(eth, ip, MTK_LRO_DIP_DW0_CFG(idx));
+ mtk_w32(eth, ip, MTK_LRO_DIP_DW0_CFG(reg_map, idx));
/* validate the IP setting */
mtk_w32(eth, (reg_val | MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(reg_map, idx));
@@ -3064,7 +3096,7 @@ static void mtk_hwlro_inval_ipaddr(struct mtk_eth *eth, int idx)
/* invalidate the IP setting */
mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(reg_map, idx));
- mtk_w32(eth, 0, MTK_LRO_DIP_DW0_CFG(idx));
+ mtk_w32(eth, 0, MTK_LRO_DIP_DW0_CFG(reg_map, idx));
}
static int mtk_hwlro_get_ip_cnt(struct mtk_mac *mac)
@@ -3080,6 +3112,65 @@ static int mtk_hwlro_get_ip_cnt(struct mtk_mac *mac)
return cnt;
}
+static int mtk_hwlro_add_ipaddr_idx(struct net_device *dev, u32 ip4dst)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ const struct mtk_reg_map *reg_map;
+ struct mtk_eth *eth = mac->hw;
+ u32 reg_val;
+ int i;
+
+ reg_map = eth->soc->reg_map;
+
+ /* check for duplicate IP address in the current DIP list */
+ for (i = 1; i <= MTK_HW_LRO_DIP_NUM(eth); i++) {
+ reg_val = mtk_r32(eth, MTK_LRO_DIP_DW0_CFG(reg_map, i));
+ if (reg_val == ip4dst)
+ break;
+ }
+
+ if (i < MTK_HW_LRO_DIP_NUM(eth) + 1) {
+ netdev_warn(dev, "Duplicate IP address at DIP(%d)!\n", i);
+ return -EEXIST;
+ }
+
+ /* find out available DIP index */
+ for (i = 1; i <= MTK_HW_LRO_DIP_NUM(eth); i++) {
+ reg_val = mtk_r32(eth, MTK_LRO_DIP_DW0_CFG(reg_map, i));
+ if (reg_val == 0UL)
+ break;
+ }
+
+ if (i >= MTK_HW_LRO_DIP_NUM(eth) + 1) {
+ netdev_warn(dev, "DIP index is currently out of resource!\n");
+ return -EBUSY;
+ }
+
+ return i;
+}
+
+static int mtk_hwlro_get_ipaddr_idx(struct net_device *dev, u32 ip4dst)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 reg_val;
+ int i;
+
+ /* find out DIP index that matches the given IP address */
+ for (i = 1; i <= MTK_HW_LRO_DIP_NUM(eth); i++) {
+ reg_val = mtk_r32(eth, MTK_LRO_DIP_DW0_CFG(eth->soc->reg_map, i));
+ if (reg_val == ip4dst)
+ break;
+ }
+
+ if (i >= MTK_HW_LRO_DIP_NUM(eth) + 1) {
+ netdev_warn(dev, "DIP address is not exist!\n");
+ return -ENOENT;
+ }
+
+ return i;
+}
+
static int mtk_hwlro_add_ipaddr(struct net_device *dev,
struct ethtool_rxnfc *cmd)
{
@@ -3088,18 +3179,22 @@ static int mtk_hwlro_add_ipaddr(struct net_device *dev,
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
int hwlro_idx;
+ u32 ip4dst;
if ((fsp->flow_type != TCP_V4_FLOW) ||
(!fsp->h_u.tcp_ip4_spec.ip4dst) ||
(fsp->location > 1))
return -EINVAL;
- mac->hwlro_ip[fsp->location] = htonl(fsp->h_u.tcp_ip4_spec.ip4dst);
- hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location;
+ ip4dst = ntohl(fsp->h_u.tcp_ip4_spec.ip4dst);
+ hwlro_idx = mtk_hwlro_add_ipaddr_idx(dev, ip4dst);
+ if (hwlro_idx < 0)
+ return hwlro_idx;
+ mac->hwlro_ip[fsp->location] = ip4dst;
mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac);
- mtk_hwlro_val_ipaddr(eth, hwlro_idx, mac->hwlro_ip[fsp->location]);
+ mtk_hwlro_val_ipaddr(eth, hwlro_idx, ip4dst);
return 0;
}
@@ -3112,13 +3207,17 @@ static int mtk_hwlro_del_ipaddr(struct net_device *dev,
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
int hwlro_idx;
+ u32 ip4dst;
if (fsp->location > 1)
return -EINVAL;
- mac->hwlro_ip[fsp->location] = 0;
- hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location;
+ ip4dst = mac->hwlro_ip[fsp->location];
+ hwlro_idx = mtk_hwlro_get_ipaddr_idx(dev, ip4dst);
+ if (hwlro_idx < 0)
+ return hwlro_idx;
+ mac->hwlro_ip[fsp->location] = 0;
mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac);
mtk_hwlro_inval_ipaddr(eth, hwlro_idx);
@@ -3126,6 +3225,24 @@ static int mtk_hwlro_del_ipaddr(struct net_device *dev,
return 0;
}
+static void mtk_hwlro_netdev_enable(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ int i, hwlro_idx;
+
+ for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
+ if (mac->hwlro_ip[i] == 0)
+ continue;
+
+ hwlro_idx = mtk_hwlro_get_ipaddr_idx(dev, mac->hwlro_ip[i]);
+ if (hwlro_idx < 0)
+ continue;
+
+ mtk_hwlro_val_ipaddr(eth, hwlro_idx, mac->hwlro_ip[i]);
+ }
+}
+
static void mtk_hwlro_netdev_disable(struct net_device *dev)
{
struct mtk_mac *mac = netdev_priv(dev);
@@ -3133,8 +3250,14 @@ static void mtk_hwlro_netdev_disable(struct net_device *dev)
int i, hwlro_idx;
for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
+ if (mac->hwlro_ip[i] == 0)
+ continue;
+
+ hwlro_idx = mtk_hwlro_get_ipaddr_idx(dev, mac->hwlro_ip[i]);
+ if (hwlro_idx < 0)
+ continue;
+
mac->hwlro_ip[i] = 0;
- hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + i;
mtk_hwlro_inval_ipaddr(eth, hwlro_idx);
}
@@ -3154,15 +3277,15 @@ static int mtk_hwlro_get_fdir_entry(struct net_device *dev,
/* only tcp dst ipv4 is meaningful, others are meaningless */
fsp->flow_type = TCP_V4_FLOW;
- fsp->h_u.tcp_ip4_spec.ip4dst = ntohl(mac->hwlro_ip[fsp->location]);
+ fsp->h_u.tcp_ip4_spec.ip4dst = htonl(mac->hwlro_ip[fsp->location]);
fsp->m_u.tcp_ip4_spec.ip4dst = 0;
fsp->h_u.tcp_ip4_spec.ip4src = 0;
- fsp->m_u.tcp_ip4_spec.ip4src = 0xffffffff;
+ fsp->m_u.tcp_ip4_spec.ip4src = htonl(~0U);
fsp->h_u.tcp_ip4_spec.psrc = 0;
- fsp->m_u.tcp_ip4_spec.psrc = 0xffff;
+ fsp->m_u.tcp_ip4_spec.psrc = htons(~0);
fsp->h_u.tcp_ip4_spec.pdst = 0;
- fsp->m_u.tcp_ip4_spec.pdst = 0xffff;
+ fsp->m_u.tcp_ip4_spec.pdst = htons(~0);
fsp->h_u.tcp_ip4_spec.tos = 0;
fsp->m_u.tcp_ip4_spec.tos = 0xff;
@@ -3314,6 +3437,8 @@ static int mtk_set_features(struct net_device *dev, netdev_features_t features)
if ((diff & NETIF_F_LRO) && !(features & NETIF_F_LRO))
mtk_hwlro_netdev_disable(dev);
+ else if ((diff & NETIF_F_LRO) && (features & NETIF_F_LRO))
+ mtk_hwlro_netdev_enable(dev);
return 0;
}
@@ -3371,8 +3496,8 @@ static int mtk_dma_init(struct mtk_eth *eth)
return err;
if (eth->hwlro) {
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) {
- err = mtk_rx_alloc(eth, i, MTK_RX_FLAGS_HWLRO);
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++) {
+ err = mtk_rx_alloc(eth, MTK_HW_LRO_RING(eth, i), MTK_RX_FLAGS_HWLRO);
if (err)
return err;
}
@@ -3434,8 +3559,8 @@ static void mtk_dma_free(struct mtk_eth *eth)
if (eth->hwlro) {
mtk_hwlro_rx_uninit(eth);
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++)
- mtk_rx_clean(eth, ð->rx_ring[i], false);
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++)
+ mtk_rx_clean(eth, ð->rx_ring[MTK_HW_LRO_RING(eth, i)], false);
}
if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) {
@@ -3630,16 +3755,21 @@ static int mtk_start_dma(struct mtk_eth *eth)
val |= MTK_RX_BT_32DWORDS;
mtk_w32(eth, val, reg_map->qdma.glo_cfg);
- mtk_w32(eth,
- MTK_RX_DMA_EN | rx_2b_offset |
- MTK_RX_BT_32DWORDS | MTK_MULTI_EN,
- reg_map->pdma.glo_cfg);
+ val = mtk_r32(eth, reg_map->pdma.glo_cfg);
+ val |= MTK_RX_DMA_EN | rx_2b_offset |
+ MTK_RX_BT_32DWORDS | MTK_MULTI_EN;
+ mtk_w32(eth, val, reg_map->pdma.glo_cfg);
} else {
mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | MTK_RX_DMA_EN |
MTK_MULTI_EN | MTK_PDMA_SIZE_8DWORDS,
reg_map->pdma.glo_cfg);
}
+ if (eth->hwlro && mtk_is_netsys_v3_or_greater(eth)) {
+ val = mtk_r32(eth, reg_map->pdma.glo_cfg);
+ mtk_w32(eth, val | MTK_RX_DMA_LRO_EN, reg_map->pdma.glo_cfg);
+ }
+
return 0;
}
@@ -3798,6 +3928,14 @@ static int mtk_open(struct net_device *dev)
}
}
+ if (eth->hwlro) {
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++) {
+ napi_enable(ð->rx_napi[MTK_HW_LRO_RING(eth, i)].napi);
+ mtk_rx_irq_enable(eth, MTK_RX_DONE_INT(eth,
+ MTK_HW_LRO_RING(eth, i)));
+ }
+ }
+
refcount_set(ð->dma_refcnt, 1);
} else {
refcount_inc(ð->dma_refcnt);
@@ -3893,6 +4031,14 @@ static int mtk_stop(struct net_device *dev)
}
}
+ if (eth->hwlro) {
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++) {
+ mtk_rx_irq_disable(eth, MTK_RX_DONE_INT(eth, MTK_HW_LRO_RING(eth, i)));
+ napi_synchronize(ð->rx_napi[MTK_HW_LRO_RING(eth, i)].napi);
+ napi_disable(ð->rx_napi[MTK_HW_LRO_RING(eth, i)].napi);
+ }
+ }
+
cancel_work_sync(ð->rx_dim.work);
cancel_work_sync(ð->tx_dim.work);
@@ -4301,6 +4447,14 @@ static int mtk_napi_init(struct mtk_eth *eth)
}
}
+ if (eth->hwlro) {
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++) {
+ rx_napi = ð->rx_napi[MTK_HW_LRO_RING(eth, i)];
+ rx_napi->eth = eth;
+ rx_napi->rx_ring = ð->rx_ring[MTK_HW_LRO_RING(eth, i)];
+ }
+ }
+
return 0;
}
@@ -5352,6 +5506,7 @@ static int mtk_probe(struct platform_device *pdev)
{
struct resource *res = NULL;
struct device_node *mac_np;
+ u8 lro_irq, lro_ring;
struct mtk_eth *eth;
char *irqname;
int err, i;
@@ -5577,6 +5732,23 @@ static int mtk_probe(struct platform_device *pdev)
goto err_free_dev;
}
}
+
+ if (eth->hwlro) {
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++) {
+ lro_irq = MTK_HW_LRO_IRQ(eth, i);
+ lro_ring = MTK_HW_LRO_RING(eth, i);
+ irqname = devm_kasprintf(eth->dev, GFP_KERNEL,
+ "%s LRO RX %d",
+ dev_name(eth->dev), i);
+ err = devm_request_irq(eth->dev,
+ eth->irq_pdma[lro_irq],
+ mtk_handle_irq_rx, IRQF_SHARED,
+ irqname,
+ ð->rx_napi[lro_ring]);
+ if (err)
+ goto err_free_dev;
+ }
+ }
} else {
irqname = devm_kasprintf(eth->dev, GFP_KERNEL, "%s RX",
dev_name(eth->dev));
@@ -5648,6 +5820,13 @@ static int mtk_probe(struct platform_device *pdev)
mtk_napi_rx);
}
+ if (eth->hwlro) {
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++) {
+ netif_napi_add(eth->dummy_dev, ð->rx_napi[MTK_HW_LRO_RING(eth, i)].napi,
+ mtk_napi_rx);
+ }
+ }
+
platform_set_drvdata(pdev, eth);
schedule_delayed_work(ð->reset.monitor_work,
MTK_DMA_MONITOR_TIMEOUT);
@@ -5696,6 +5875,12 @@ static void mtk_remove(struct platform_device *pdev)
for (i = 1; i < MTK_RX_RSS_NUM(eth); i++)
netif_napi_del(ð->rx_napi[MTK_RSS_RING(i)].napi);
}
+
+ if (eth->hwlro) {
+ for (i = 0; i < MTK_HW_LRO_RING_NUM(eth); i++)
+ netif_napi_del(ð->rx_napi[MTK_HW_LRO_RING(eth, i)].napi);
+ }
+
mtk_cleanup(eth);
free_netdev(eth->dummy_dev);
mtk_mdio_cleanup(eth);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 378cf47913ef..f7e7299fef6b 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -35,7 +35,7 @@
#define MTK_DMA_SIZE(x) (SZ_##x)
#define MTK_FQ_DMA_HEAD 32
#define MTK_FQ_DMA_LENGTH 2048
-#define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN)
+#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
#define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
#define MTK_DMA_DUMMY_DESC 0xffffffff
#define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
@@ -63,10 +63,9 @@
#define MTK_QRX_OFFSET 0x10
-#define MTK_MAX_RX_RING_NUM 4
-#define MTK_HW_LRO_DMA_SIZE 8
-
-#define MTK_MAX_LRO_RX_LENGTH (4096 * 3)
+#define MTK_MAX_RX_RING_NUM (8)
+#define MTK_HW_LRO_DMA_SIZE(eth) (mtk_is_netsys_v3_or_greater(eth) ? 64 : 8)
+#define MTK_MAX_LRO_RX_LENGTH (4096 * 3 + MTK_MAX_RX_LENGTH)
#define MTK_MAX_LRO_IP_CNT 2
#define MTK_HW_LRO_TIMER_UNIT 1 /* 20 us */
#define MTK_HW_LRO_REFRESH_TIME 50000 /* 1 sec. */
@@ -182,31 +181,39 @@
#define MTK_CDMM_THRES 0x165c
/* PDMA HW LRO Control Registers */
-#define MTK_PDMA_LRO_CTRL_DW0 0x980
-#define MTK_HW_LRO_RING_NUM(eth) (mtk_is_netsys_v3_or_greater(eth) ? 4 : 3)
+#define MTK_HW_LRO_DIP_NUM(eth) (mtk_is_netsys_v3_or_greater(eth) ? 4 : 3)
+#define MTK_HW_LRO_RING_NUM(eth) (mtk_is_netsys_v3_or_greater(eth) ? 4 : 3)
+#define MTK_HW_LRO_RING(eth, x) ((x) + (mtk_is_netsys_v3_or_greater(eth) ? 4 : 1))
+#define MTK_HW_LRO_IRQ(eth, x) ((x) + (mtk_is_netsys_v3_or_greater(eth) ? 0 : 1))
+#define MTK_LRO_CRSN_BNW(eth) BIT((mtk_is_netsys_v3_or_greater(eth) ? 22 : 6))
#define MTK_LRO_EN BIT(0)
#define MTK_NON_LRO_MULTI_EN BIT(2)
#define MTK_LRO_DLY_INT_EN BIT(5)
-#define MTK_L3_CKS_UPD_EN BIT(7)
-#define MTK_L3_CKS_UPD_EN_V2 BIT(19)
+#define MTK_L3_CKS_UPD_EN(eth) BIT(mtk_is_netsys_v3_or_greater(eth) ? 19 : 7)
#define MTK_LRO_ALT_PKT_CNT_MODE BIT(21)
-#define MTK_LRO_RING_RELINQUISH_REQ (0x7 << 26)
-#define MTK_LRO_RING_RELINQUISH_REQ_V2 (0xf << 24)
-#define MTK_LRO_RING_RELINQUISH_DONE (0x7 << 29)
-#define MTK_LRO_RING_RELINQUISH_DONE_V2 (0xf << 28)
-
-#define MTK_PDMA_LRO_CTRL_DW1 0x984
-#define MTK_PDMA_LRO_CTRL_DW2 0x988
-#define MTK_PDMA_LRO_CTRL_DW3 0x98c
+#define MTK_LRO_RING_RELINQUISH_REQ(eth) (mtk_is_netsys_v3_or_greater(eth) ? \
+ 0xf << 24 : 0x7 << 26)
+#define MTK_LRO_RING_RELINQUISH_DONE(eth) (mtk_is_netsys_v3_or_greater(eth) ? \
+ 0xf << 28 : 0x7 << 29)
+
+#define MTK_PDMA_LRO_CTRL_DW0(reg_map) ((reg_map)->pdma.lro_ctrl_dw0)
+#define MTK_PDMA_LRO_CTRL_DW1(reg_map) ((reg_map)->pdma.lro_ctrl_dw0 + 0x04)
+#define MTK_PDMA_LRO_CTRL_DW2(reg_map) ((reg_map)->pdma.lro_ctrl_dw0 + 0x08)
+#define MTK_PDMA_LRO_CTRL_DW3(reg_map) ((reg_map)->pdma.lro_ctrl_dw0 + 0x0c)
+#define MTK_LRO_VLAN_EN (0xf << 8)
+#define MTK_LRO_VLAN_VID_CMP_DEPTH (0x3 << 12)
+#define MTK_LRO_L4_CTRL_PSH_EN BIT(23)
#define MTK_ADMA_MODE BIT(15)
#define MTK_LRO_MIN_RXD_SDL (MTK_HW_LRO_SDL_REMAIN_ROOM << 16)
+#define MTK_CTRL_DW0_SDL_OFFSET (3)
+
#define MTK_RX_DMA_LRO_EN BIT(8)
#define MTK_MULTI_EN BIT(10)
#define MTK_PDMA_SIZE_8DWORDS (1 << 4)
/* PDMA RSS Control Registers */
-#define MTK_RX_NAPI_NUM (4)
+#define MTK_RX_NAPI_NUM (8)
#define MTK_RX_RSS_NUM(eth) ((eth)->soc->rss_num)
#define MTK_RSS_RING(x) (x)
#define MTK_RSS_EN BIT(0)
@@ -242,11 +249,10 @@
#define MTK_PDMA_DELAY_PTIME_MASK 0xff
/* PDMA HW LRO Alter Flow Delta Register */
-#define MTK_PDMA_LRO_ALT_SCORE_DELTA 0xa4c
+#define MTK_PDMA_LRO_ALT_SCORE_DELTA(reg_map) ((reg_map)->pdma.lro_alt_score_delta)
/* PDMA HW LRO IP Setting Registers */
-#define MTK_LRO_RX_RING0_DIP_DW0 0xb04
-#define MTK_LRO_DIP_DW0_CFG(x) (MTK_LRO_RX_RING0_DIP_DW0 + (x * 0x40))
+#define MTK_LRO_DIP_DW0_CFG(reg_map, x) ((reg_map)->pdma.lro_ring_dip_dw0 + ((x) * 0x40))
#define MTK_RING_MYIP_VLD BIT(9)
/* PDMA HW LRO Ring Control Registers */
@@ -1187,7 +1193,8 @@ enum mkt_eth_capabilities {
#define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_GMAC2_2P5GPHY | \
MTK_MUX_GMAC2_TO_2P5GPHY | MTK_QDMA | MTK_RSTCTRL_PPE1 | \
- MTK_RSTCTRL_PPE2 | MTK_SRAM | MTK_PDMA_INT | MTK_RSS)
+ MTK_RSTCTRL_PPE2 | MTK_SRAM | MTK_PDMA_INT | MTK_RSS | \
+ MTK_HWLRO)
struct mtk_tx_dma_desc_info {
dma_addr_t addr;
@@ -1454,7 +1461,7 @@ struct mtk_mac {
struct phylink_config phylink_config;
struct mtk_eth *hw;
struct mtk_hw_stats *hw_stats;
- __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
+ u32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
int hwlro_ip_cnt;
unsigned int syscfg0;
struct notifier_block device_notifier;
--
2.43.0
^ permalink raw reply related
* [net-next v8 0/3] Add RSS and LRO support
From: Frank Wunderlich @ 2026-05-09 19:09 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Russell King
Cc: Frank Wunderlich, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek, Mason Chang, Daniel Golle
From: Frank Wunderlich <frank-w@public-files.de>
This series add RSS and LRO hardware acceleration for terminating
traffic on MT798x.
patches are upported from mtk SDK:
- https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/refs/heads/master/master/files/target/linux/mediatek/patches-6.12/999-eth-08-mtk_eth_soc-add-register-definitions-for-rss-lro-reg.patch
- https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/refs/heads/master/master/files/target/linux/mediatek/patches-6.12/999-eth-09-mtk_eth_soc-add-rss-support.patch
- https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/refs/heads/master/master/files/target/linux/mediatek/patches-6.12/999-eth-10-mtk_eth_soc-add-hw-lro-support.patch
with additional fixes
changes:
v8:
- fix more sparse errors, should be all touched lines now
v7:
- fix u32 vs. be32 reported by patchwork check
- add L4 PSH check
https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/7521c42b0bd5be20d52e20b110daea8c756fc069%5E%21/#F1
- Add HW LRO max 4-depth VLAN support including switch special tag.
https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/35490cec6a2e5982532935fb0a1c884f7c4efdb0%5E%21/#F2
v6:
- no RFC
- rebase on netnext (7.1-rc1)
- drop unused MTK_CTRL_DW0_SDL_MASK
- e33bd8dd7f1f ("net: mediatek: convert to use .get_rx_ring_count") moved
ETHTOOL_GRXRINGS handling from mtk_get_rxnfc to mtk_get_rx_ring_count
move changes to this new function too
- fix some Macro argument '...' may be better as '(...)' to avoid precedence issues
v5:
- fix too long lines after macro changes reported by checkpatch
v4:
- drop unrelated file
- rss-changes suggested by andrew
- fix MTK_HW_LRO_RING_NUM macro (add eth)
- fix MTK_LRO_CTRL_DW[123]_CFG (add reg_map param)
- fix MTK_RX_DONE_INT (add eth param)
- fix lro reverse christmas tree and LRO params suggested by andrew
- drop mtk_hwlro_stats_ebl and unused IS_HW_LRO_RING (only used in
properitary debugfs)
v3:
- readded the change dropped in v2 because it was a fix
for getting RSS working on mt7986
- changes requested by jakub
- reworked coverletter (dropped instructions for configuration)
- name all PDMA-IRQ the same way
- retested on
- BPI-R3/mt7986 (RSS needs to be enabled)
- BPI-R4/mt7988
- BPI-R64/mt7622 and BPI-R2/mt7623 for not breaking network functionality
v2:
- drop wrong change (MTK_CDMP_IG_CTRL is only netsys v1)
- Fix immutable string IRQ setup (thx to Emilia Schotte)
- drop links to 6.6 patches/commits in sdk in comments
Mason Chang (3):
net: ethernet: mtk_eth_soc: Add register definitions for RSS and LRO
net: ethernet: mtk_eth_soc: Add RSS support
net: ethernet: mtk_eth_soc: Add LRO support
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 828 ++++++++++++++++----
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 177 +++--
2 files changed, 792 insertions(+), 213 deletions(-)
--
2.43.0
^ permalink raw reply
* [net-next v8 1/3] net: ethernet: mtk_eth_soc: Add register definitions for RSS and LRO
From: Frank Wunderlich @ 2026-05-09 19:09 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Russell King
Cc: Frank Wunderlich, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek, Mason Chang, Daniel Golle
In-Reply-To: <20260509190938.169290-1-linux@fw-web.de>
From: Mason Chang <mason-cw.chang@mediatek.com>
Add definitions for Receive Side Scaling and Large Receive Offload support.
Signed-off-by: Mason Chang <mason-cw.chang@mediatek.com>
Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
---
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 23 +++++++++++++++
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 32 +++++++++++++++------
2 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 8d225bc9f063..d25e0b96c26e 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -50,13 +50,18 @@ static const struct mtk_reg_map mtk_reg_map = {
.rx_ptr = 0x0900,
.rx_cnt_cfg = 0x0904,
.pcrx_ptr = 0x0908,
+ .lro_ctrl_dw0 = 0x0980,
.glo_cfg = 0x0a04,
.rst_idx = 0x0a08,
.delay_irq = 0x0a0c,
.irq_status = 0x0a20,
.irq_mask = 0x0a28,
.adma_rx_dbg0 = 0x0a38,
+ .lro_alt_score_delta = 0x0a4c,
.int_grp = 0x0a50,
+ .lro_rx1_dly_int = 0x0a70,
+ .lro_ring_dip_dw0 = 0x0b04,
+ .lro_ring_ctrl_dw1 = 0x0b28,
},
.qdma = {
.qtx_cfg = 0x1800,
@@ -113,6 +118,7 @@ static const struct mtk_reg_map mt7986_reg_map = {
.tx_irq_mask = 0x461c,
.tx_irq_status = 0x4618,
.pdma = {
+ .rss_glo_cfg = 0x2800,
.rx_ptr = 0x4100,
.rx_cnt_cfg = 0x4104,
.pcrx_ptr = 0x4108,
@@ -123,6 +129,12 @@ static const struct mtk_reg_map mt7986_reg_map = {
.irq_mask = 0x4228,
.adma_rx_dbg0 = 0x4238,
.int_grp = 0x4250,
+ .int_grp3 = 0x422c,
+ .lro_ctrl_dw0 = 0x4180,
+ .lro_alt_score_delta = 0x424c,
+ .lro_rx1_dly_int = 0x4270,
+ .lro_ring_dip_dw0 = 0x4304,
+ .lro_ring_ctrl_dw1 = 0x4328,
},
.qdma = {
.qtx_cfg = 0x4400,
@@ -170,10 +182,21 @@ static const struct mtk_reg_map mt7988_reg_map = {
.glo_cfg = 0x6a04,
.rst_idx = 0x6a08,
.delay_irq = 0x6a0c,
+ .rx_cfg = 0x6a10,
.irq_status = 0x6a20,
.irq_mask = 0x6a28,
.adma_rx_dbg0 = 0x6a38,
.int_grp = 0x6a50,
+ .int_grp3 = 0x6a58,
+ .tx_delay_irq = 0x6ab0,
+ .rx_delay_irq = 0x6ac0,
+ .lro_ctrl_dw0 = 0x6c08,
+ .lro_alt_score_delta = 0x6c1c,
+ .lro_ring_dip_dw0 = 0x6c14,
+ .lro_ring_ctrl_dw1 = 0x6c38,
+ .lro_alt_dbg = 0x6c40,
+ .lro_alt_dbg_data = 0x6c44,
+ .rss_glo_cfg = 0x7000,
},
.qdma = {
.qtx_cfg = 0x4400,
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 0168e2fbc619..334625814b97 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -1143,16 +1143,30 @@ struct mtk_reg_map {
u32 tx_irq_mask;
u32 tx_irq_status;
struct {
- u32 rx_ptr; /* rx base pointer */
- u32 rx_cnt_cfg; /* rx max count configuration */
- u32 pcrx_ptr; /* rx cpu pointer */
- u32 glo_cfg; /* global configuration */
- u32 rst_idx; /* reset index */
- u32 delay_irq; /* delay interrupt */
- u32 irq_status; /* interrupt status */
- u32 irq_mask; /* interrupt mask */
+ u32 rx_ptr; /* rx base pointer */
+ u32 rx_cnt_cfg; /* rx max count configuration */
+ u32 pcrx_ptr; /* rx cpu pointer */
+ u32 pdrx_ptr; /* rx dma pointer */
+ u32 glo_cfg; /* global configuration */
+ u32 rst_idx; /* reset index */
+ u32 rx_cfg; /* rx dma configuration */
+ u32 delay_irq; /* delay interrupt */
+ u32 irq_status; /* interrupt status */
+ u32 irq_mask; /* interrupt mask */
u32 adma_rx_dbg0;
- u32 int_grp;
+ u32 int_grp; /* interrupt group1 */
+ u32 int_grp3; /* interrupt group3 */
+ u32 tx_delay_irq; /* tx delay interrupt */
+ u32 rx_delay_irq; /* rx delay interrupt */
+ u32 lro_ctrl_dw0; /* lro ctrl dword0 */
+ u32 lro_alt_score_delta; /* lro auto-learn score delta */
+ u32 lro_rx1_dly_int; /* lro rx ring1 delay interrupt */
+ u32 lro_ring_dip_dw0; /* lro ring dip dword0 */
+ u32 lro_ring_ctrl_dw1; /* lro ring ctrl dword1 */
+ u32 lro_alt_dbg; /* lro auto-learn debug */
+ u32 lro_alt_dbg_data; /* lro auto-learn debug data */
+ u32 rss_glo_cfg; /* rss global configuration */
+
} pdma;
struct {
u32 qtx_cfg; /* tx queue configuration */
--
2.43.0
^ permalink raw reply related
* [PATCH v2 8/8] drm/sun4i: switch DE33 to new bindings
From: Jernej Skrabec @ 2026-05-09 19:00 UTC (permalink / raw)
To: wens, samuel
Cc: mripard, maarten.lankhorst, tzimmermann, airlied, simona, robh,
krzk+dt, conor+dt, mturquette, sboyd, dri-devel, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk,
jernej.skrabec
In-Reply-To: <20260509190015.79086-1-jernej.skrabec@siol.net>
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Now that everything is in place, switch DE33 to new bindings.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
Changes from v1:
- remove CONFIG_DRM_SUN50I_PLANES guard
- fix error path
drivers/gpu/drm/sun4i/sun8i_mixer.c | 131 +++++++++++++++-------------
drivers/gpu/drm/sun4i/sun8i_mixer.h | 10 +--
2 files changed, 72 insertions(+), 69 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 4071ab38b4ae..957db0938455 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@@ -25,6 +26,7 @@
#include <drm/drm_probe_helper.h>
#include "sun4i_drv.h"
+#include "sun50i_planes.h"
#include "sun8i_mixer.h"
#include "sun8i_ui_layer.h"
#include "sun8i_vi_layer.h"
@@ -257,7 +259,6 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine,
{
struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
u32 bld_base = sun8i_blender_base(mixer);
- struct regmap *bld_regs = sun8i_blender_regmap(mixer);
struct drm_plane_state *plane_state;
struct drm_plane *plane;
u32 route = 0, pipe_en = 0;
@@ -294,16 +295,16 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine,
route |= layer->index << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
- regmap_write(bld_regs,
+ regmap_write(engine->regs,
SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
SUN8I_MIXER_COORD(x, y));
- regmap_write(bld_regs,
+ regmap_write(engine->regs,
SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
SUN8I_MIXER_SIZE(w, h));
}
- regmap_write(bld_regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route);
- regmap_write(bld_regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
+ regmap_write(engine->regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route);
+ regmap_write(engine->regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
if (mixer->cfg->de_type != SUN8I_MIXER_DE33)
@@ -318,7 +319,6 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
int plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
enum drm_plane_type type;
- unsigned int phy_index;
int i;
planes = devm_kcalloc(drm->dev, plane_cnt + 1, sizeof(*planes), GFP_KERNEL);
@@ -333,12 +333,8 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
else
type = DRM_PLANE_TYPE_OVERLAY;
- phy_index = i;
- if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
- phy_index = mixer->cfg->map[i];
-
layer = sun8i_vi_layer_init_one(drm, type, mixer->engine.regs,
- i, phy_index, plane_cnt,
+ i, i, plane_cnt,
&mixer->cfg->lay_cfg);
if (IS_ERR(layer)) {
dev_err(drm->dev,
@@ -358,12 +354,8 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
else
type = DRM_PLANE_TYPE_OVERLAY;
- phy_index = index;
- if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
- phy_index = mixer->cfg->map[index];
-
layer = sun8i_ui_layer_init_one(drm, type, mixer->engine.regs,
- index, phy_index, plane_cnt,
+ index, index, plane_cnt,
&mixer->cfg->lay_cfg);
if (IS_ERR(layer)) {
dev_err(drm->dev, "Couldn't initialize %s plane\n",
@@ -377,16 +369,22 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
return planes;
}
+static struct drm_plane **sun50i_layers_init(struct drm_device *drm,
+ struct sunxi_engine *engine)
+{
+ struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
+
+ return sun50i_planes_setup(mixer->planes_dev, drm, engine->id);
+}
+
static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
const struct drm_display_mode *mode)
{
struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
- struct regmap *bld_regs;
u32 bld_base, size, val;
bool interlaced;
bld_base = sun8i_blender_base(mixer);
- bld_regs = sun8i_blender_regmap(mixer);
interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay);
@@ -398,14 +396,14 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
else
regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, size);
- regmap_write(bld_regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
+ regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
if (interlaced)
val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
else
val = 0;
- regmap_update_bits(bld_regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
+ regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val);
DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
@@ -418,8 +416,14 @@ static const struct sunxi_engine_ops sun8i_engine_ops = {
.mode_set = sun8i_mixer_mode_set,
};
+static const struct sunxi_engine_ops sun50i_engine_ops = {
+ .commit = sun8i_mixer_commit,
+ .layers_init = sun50i_layers_init,
+ .mode_set = sun8i_mixer_mode_set,
+};
+
static const struct regmap_config sun8i_mixer_regmap_config = {
- .name = "layers",
+ .name = "display",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
@@ -434,14 +438,6 @@ static const struct regmap_config sun8i_top_regmap_config = {
.max_register = 0x3c,
};
-static const struct regmap_config sun8i_disp_regmap_config = {
- .name = "display",
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = 0x20000,
-};
-
static int sun8i_mixer_of_get_id(struct device_node *node)
{
struct device_node *ep, *remote;
@@ -464,17 +460,14 @@ static int sun8i_mixer_of_get_id(struct device_node *node)
static void sun8i_mixer_init(struct sun8i_mixer *mixer)
{
- struct regmap *top_regs, *disp_regs;
unsigned int base = sun8i_blender_base(mixer);
+ struct regmap *top_regs;
int plane_cnt, i;
- if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
+ if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
top_regs = mixer->top_regs;
- disp_regs = mixer->disp_regs;
- } else {
+ else
top_regs = mixer->engine.regs;
- disp_regs = mixer->engine.regs;
- }
/* Enable the mixer */
regmap_write(top_regs, SUN8I_MIXER_GLOBAL_CTL,
@@ -484,25 +477,25 @@ static void sun8i_mixer_init(struct sun8i_mixer *mixer)
regmap_write(top_regs, SUN50I_MIXER_GLOBAL_CLK, 1);
/* Set background color to black */
- regmap_write(disp_regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
SUN8I_MIXER_BLEND_COLOR_BLACK);
/*
* Set fill color of bottom plane to black. Generally not needed
* except when VI plane is at bottom (zpos = 0) and enabled.
*/
- regmap_write(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
- regmap_write(disp_regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
SUN8I_MIXER_BLEND_COLOR_BLACK);
plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
for (i = 0; i < plane_cnt; i++)
- regmap_write(disp_regs,
+ regmap_write(mixer->engine.regs,
SUN8I_MIXER_BLEND_MODE(base, i),
SUN8I_MIXER_BLEND_MODE_DEF);
- regmap_update_bits(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
+ regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0);
}
@@ -533,7 +526,6 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
if (!mixer)
return -ENOMEM;
dev_set_drvdata(dev, mixer);
- mixer->engine.ops = &sun8i_engine_ops;
mixer->engine.node = dev->of_node;
/*
@@ -566,6 +558,11 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
if (!mixer->cfg)
return -EINVAL;
+ if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
+ mixer->engine.ops = &sun50i_engine_ops;
+ else
+ mixer->engine.ops = &sun8i_engine_ops;
+
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
@@ -588,17 +585,6 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
dev_err(dev, "Couldn't create the top regmap\n");
return PTR_ERR(mixer->top_regs);
}
-
- regs = devm_platform_ioremap_resource_byname(pdev, "display");
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- mixer->disp_regs = devm_regmap_init_mmio(dev, regs,
- &sun8i_disp_regmap_config);
- if (IS_ERR(mixer->disp_regs)) {
- dev_err(dev, "Couldn't create the disp regmap\n");
- return PTR_ERR(mixer->disp_regs);
- }
}
mixer->reset = devm_reset_control_get(dev, NULL);
@@ -638,6 +624,34 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
clk_prepare_enable(mixer->mod_clk);
+ if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
+ struct platform_device *pdev;
+ struct device_node *np;
+ void *data;
+
+ np = of_parse_phandle(dev->of_node, "allwinner,planes", 0);
+ if (!np) {
+ ret = -ENODEV;
+ goto err_disable_mod_clk;
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ ret = -EPROBE_DEFER;
+ goto err_disable_mod_clk;
+ }
+
+ data = platform_get_drvdata(pdev);
+ if (!data) {
+ put_device(&pdev->dev);
+ ret = -EPROBE_DEFER;
+ goto err_disable_mod_clk;
+ }
+
+ mixer->planes_dev = &pdev->dev;
+ }
+
list_add_tail(&mixer->engine.list, &drv->engine_list);
/* Reset registers and disable unused sub-engines */
@@ -672,6 +686,8 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
return 0;
+err_disable_mod_clk:
+ clk_disable_unprepare(mixer->mod_clk);
err_disable_bus_clk:
clk_disable_unprepare(mixer->bus_clk);
err_assert_reset:
@@ -686,6 +702,9 @@ static void sun8i_mixer_unbind(struct device *dev, struct device *master,
list_del(&mixer->engine.list);
+ if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
+ put_device(mixer->planes_dev);
+
clk_disable_unprepare(mixer->mod_clk);
clk_disable_unprepare(mixer->bus_clk);
reset_control_assert(mixer->reset);
@@ -867,16 +886,8 @@ static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
};
static const struct sun8i_mixer_cfg sun50i_h616_mixer0_cfg = {
- .lay_cfg = {
- .de_type = SUN8I_MIXER_DE33,
- .scaler_mask = 0xf,
- .scanline_yuv = 4096,
- },
.de_type = SUN8I_MIXER_DE33,
.mod_rate = 600000000,
- .ui_num = 3,
- .vi_num = 1,
- .map = {0, 6, 7, 8},
};
static const struct of_device_id sun8i_mixer_of_table[] = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index e2f83301aae8..7abc88c898d9 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -202,7 +202,6 @@ struct sun8i_mixer_cfg {
int ui_num;
unsigned int de_type;
unsigned long mod_rate;
- unsigned int map[6];
};
struct sun8i_mixer {
@@ -216,7 +215,7 @@ struct sun8i_mixer {
struct clk *mod_clk;
struct regmap *top_regs;
- struct regmap *disp_regs;
+ struct device *planes_dev;
};
enum {
@@ -252,13 +251,6 @@ sun8i_blender_base(struct sun8i_mixer *mixer)
return mixer->cfg->de_type == SUN8I_MIXER_DE3 ? DE3_BLD_BASE : DE2_BLD_BASE;
}
-static inline struct regmap *
-sun8i_blender_regmap(struct sun8i_mixer *mixer)
-{
- return mixer->cfg->de_type == SUN8I_MIXER_DE33 ?
- mixer->disp_regs : mixer->engine.regs;
-}
-
static inline u32
sun8i_channel_base(struct sun8i_layer *layer)
{
--
2.54.0
^ permalink raw reply related
* [PATCH v2 6/8] drm/sun4i: Add planes driver
From: Jernej Skrabec @ 2026-05-09 19:00 UTC (permalink / raw)
To: wens, samuel
Cc: mripard, maarten.lankhorst, tzimmermann, airlied, simona, robh,
krzk+dt, conor+dt, mturquette, sboyd, dri-devel, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk,
jernej.skrabec
In-Reply-To: <20260509190015.79086-1-jernej.skrabec@siol.net>
From: Jernej Skrabec <jernej.skrabec@gmail.com>
This driver serves just as planes sharing manager, needed for Display
Engine 3.3 and newer.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
Changes from v1:
- removed CONFIG_DRM_SUN50I_PLANES Kconfig entirely
- make sun50i_planes_of_table[] static
- folded sun50i_planes_node_is_planes() into sun50i_planes_setup()
- sun50i_planes_setup() error returns now ERR_PTR(-EINVAL) instead of NULL
at first three checks
- quirks and of_device_id table moved on top to avoid forward declaration
drivers/gpu/drm/sun4i/Makefile | 3 +-
drivers/gpu/drm/sun4i/sun50i_planes.c | 201 ++++++++++++++++++++++++++
drivers/gpu/drm/sun4i/sun50i_planes.h | 41 ++++++
3 files changed, 244 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/sun4i/sun50i_planes.c
create mode 100644 drivers/gpu/drm/sun4i/sun50i_planes.h
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index bad7497a0d11..501e3d867918 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -16,7 +16,8 @@ sun8i-drm-hdmi-y += sun8i_hdmi_phy_clk.o
sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \
sun8i_vi_layer.o sun8i_ui_scaler.o \
- sun8i_vi_scaler.o sun8i_csc.o
+ sun8i_vi_scaler.o sun8i_csc.o \
+ sun50i_planes.o
sun4i-tcon-y += sun4i_crtc.o
sun4i-tcon-y += sun4i_tcon_dclk.o
diff --git a/drivers/gpu/drm/sun4i/sun50i_planes.c b/drivers/gpu/drm/sun4i/sun50i_planes.c
new file mode 100644
index 000000000000..6469de1baf03
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun50i_planes.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2025 Jernej Skrabec <jernej.skrabec@gmail.com> */
+
+#include <drm/drm_device.h>
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "sun50i_planes.h"
+#include "sun8i_ui_layer.h"
+#include "sun8i_vi_layer.h"
+
+static const struct sun50i_planes_quirks sun50i_h616_planes_quirks = {
+ .def_map = {
+ {
+ .map = {0, 6, 7},
+ .num_ch = 3,
+ },
+ {
+ .map = {1, 2, 8},
+ .num_ch = 3,
+ },
+ },
+ .cfg = {
+ .de_type = SUN8I_MIXER_DE33,
+ /*
+ * TODO: All planes support scaling, but driver needs
+ * improvements to properly support it.
+ */
+ .scaler_mask = 0,
+ .scanline_yuv = 4096,
+ },
+};
+
+static const struct of_device_id sun50i_planes_of_table[] = {
+ {
+ .compatible = "allwinner,sun50i-h616-de33-planes",
+ .data = &sun50i_h616_planes_quirks
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun50i_planes_of_table);
+
+struct drm_plane **
+sun50i_planes_setup(struct device *dev, struct drm_device *drm,
+ unsigned int mixer)
+{
+ struct sun50i_planes *planes = dev_get_drvdata(dev);
+ const struct sun50i_planes_quirks *quirks;
+ struct drm_plane **drm_planes;
+ const struct default_map *map;
+ unsigned int i;
+
+ if (!of_match_node(sun50i_planes_of_table, dev->of_node)) {
+ dev_err(dev, "Device is not planes driver!\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!planes) {
+ dev_err(dev, "Planes driver is not loaded yet!\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (mixer > 1) {
+ dev_err(dev, "Mixer index is too high!\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ quirks = planes->quirks;
+ map = &quirks->def_map[mixer];
+
+ drm_planes = devm_kcalloc(drm->dev, map->num_ch + 1,
+ sizeof(*drm_planes), GFP_KERNEL);
+ if (!drm_planes)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < map->num_ch; i++) {
+ unsigned int phy_ch = map->map[i];
+ struct sun8i_layer *layer;
+ enum drm_plane_type type;
+
+ if ((i == 0 && map->num_ch == 1) || i == 1)
+ type = DRM_PLANE_TYPE_PRIMARY;
+ else
+ type = DRM_PLANE_TYPE_OVERLAY;
+
+ if (phy_ch < UI_PLANE_OFFSET)
+ layer = sun8i_vi_layer_init_one(drm, type, planes->regs,
+ i, phy_ch, map->num_ch,
+ &quirks->cfg);
+ else
+ layer = sun8i_ui_layer_init_one(drm, type, planes->regs,
+ i, phy_ch, map->num_ch,
+ &quirks->cfg);
+
+ if (IS_ERR(layer)) {
+ dev_err(drm->dev,
+ "Couldn't initialize DRM plane\n");
+ return ERR_CAST(layer);
+ }
+
+ drm_planes[i] = &layer->plane;
+ }
+
+ return drm_planes;
+}
+EXPORT_SYMBOL(sun50i_planes_setup);
+
+static void sun50i_planes_init_mapping(struct sun50i_planes *planes)
+{
+ const struct sun50i_planes_quirks *quirks = planes->quirks;
+ unsigned int i, j;
+ u32 mapping;
+
+ mapping = 0;
+ for (j = 0; j < MAX_DISP; j++)
+ for (i = 0; i < quirks->def_map[j].num_ch; i++) {
+ unsigned int ch = quirks->def_map[j].map[i];
+
+ if (ch < UI_PLANE_OFFSET)
+ mapping |= j << (ch * 2);
+ else
+ mapping |= j << ((ch - UI_PLANE_OFFSET) * 2 + 16);
+ }
+ regmap_write(planes->mapping, SUNXI_DE33_DE_CHN2CORE_MUX_REG, mapping);
+
+ for (j = 0; j < MAX_DISP; j++) {
+ mapping = 0;
+ for (i = 0; i < quirks->def_map[j].num_ch; i++) {
+ unsigned int ch = quirks->def_map[j].map[i];
+
+ if (ch >= UI_PLANE_OFFSET)
+ ch += 2;
+
+ mapping |= ch << (i * 4);
+ }
+ regmap_write(planes->mapping, SUNXI_DE33_DE_PORT02CHN_MUX_REG + j * 4, mapping);
+ }
+}
+
+static const struct regmap_config sun50i_planes_regmap_config = {
+ .name = "planes",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x17fffc,
+};
+
+static int sun50i_planes_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sun50i_planes *planes;
+ void __iomem *regs;
+
+ planes = devm_kzalloc(dev, sizeof(*planes), GFP_KERNEL);
+ if (!planes)
+ return -ENOMEM;
+
+ planes->quirks = of_device_get_match_data(&pdev->dev);
+ if (!planes->quirks)
+ return dev_err_probe(dev, -EINVAL, "Unable to get quirks\n");
+
+ planes->mapping = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "allwinner,plane-mapping");
+ if (IS_ERR(planes->mapping))
+ return dev_err_probe(dev, PTR_ERR(planes->mapping),
+ "Unable to get mapping\n");
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ planes->regs = devm_regmap_init_mmio(dev, regs, &sun50i_planes_regmap_config);
+ if (IS_ERR(planes->regs))
+ return PTR_ERR(planes->regs);
+
+ dev_set_drvdata(dev, planes);
+
+ sun50i_planes_init_mapping(planes);
+
+ return 0;
+}
+
+static struct platform_driver sun50i_planes_platform_driver = {
+ .probe = sun50i_planes_probe,
+ .driver = {
+ .name = "sun50i-planes",
+ .of_match_table = sun50i_planes_of_table,
+ },
+};
+module_platform_driver(sun50i_planes_platform_driver);
+
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
+MODULE_DESCRIPTION("Allwinner DE33 planes driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun50i_planes.h b/drivers/gpu/drm/sun4i/sun50i_planes.h
new file mode 100644
index 000000000000..e5b54ed16178
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun50i_planes.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2025 Jernej Skrabec <jernej.skrabec@gmail.com> */
+
+#ifndef _SUN50I_PLANES_H_
+#define _SUN50I_PLANES_H_
+
+#include "sun8i_mixer.h"
+
+/* mapping registers, located in clock register space */
+#define SUNXI_DE33_DE_CHN2CORE_MUX_REG 0x24
+#define SUNXI_DE33_DE_PORT02CHN_MUX_REG 0x28
+#define SUNXI_DE33_DE_PORT12CHN_MUX_REG 0x2c
+
+#define MAX_DISP 2
+#define MAX_CHANNELS 8
+#define UI_PLANE_OFFSET 6
+
+struct regmap;
+struct drm_device;
+
+struct default_map {
+ unsigned int map[MAX_CHANNELS];
+ unsigned int num_ch;
+};
+
+struct sun50i_planes_quirks {
+ struct default_map def_map[MAX_DISP];
+ struct sun8i_layer_cfg cfg;
+};
+
+struct sun50i_planes {
+ struct regmap *regs;
+ struct regmap *mapping;
+ const struct sun50i_planes_quirks *quirks;
+};
+
+struct drm_plane **
+sun50i_planes_setup(struct device *dev, struct drm_device *drm,
+ unsigned int mixer);
+
+#endif /* _SUN50I_PLANES_H_ */
--
2.54.0
^ permalink raw reply related
* [PATCH v2 4/8] drm/sun4i: vi_layer: Limit formats for DE33
From: Jernej Skrabec @ 2026-05-09 19:00 UTC (permalink / raw)
To: wens, samuel
Cc: mripard, maarten.lankhorst, tzimmermann, airlied, simona, robh,
krzk+dt, conor+dt, mturquette, sboyd, dri-devel, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk,
jernej.skrabec, Chen-Yu Tsai
In-Reply-To: <20260509190015.79086-1-jernej.skrabec@siol.net>
From: Jernej Skrabec <jernej.skrabec@gmail.com>
YUV formats need scaler support due to chroma upscaling, but that's not
yet supported in the driver. Remove them from supported list until
DE33 scaler is properly supported.
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
Changes from v1:
- collected tag
- generalize check to be dependant on scaler feature
drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 40 ++++++++++++++++++++++++--
1 file changed, 38 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index cd8d6c2da0c7..afbd41a18a0e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -380,6 +380,33 @@ static const u32 sun8i_vi_layer_de3_formats[] = {
DRM_FORMAT_YVU422,
};
+static const u32 sun8i_vi_layer_de3_no_yuv_formats[] = {
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_ABGR4444,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_BGRA1010102,
+ DRM_FORMAT_BGRA5551,
+ DRM_FORMAT_BGRA4444,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGBA1010102,
+ DRM_FORMAT_RGBA4444,
+ DRM_FORMAT_RGBA5551,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_XRGB8888,
+};
+
static const uint64_t sun8i_layer_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
@@ -410,8 +437,17 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
layer->cfg = cfg;
if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
- formats = sun8i_vi_layer_de3_formats;
- format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
+ /*
+ * TODO: DE33 drivers doesn't support scaling yet, which is a
+ * requirement for YUV support.
+ */
+ if (layer->cfg->scaler_mask & BIT(phy_index)) {
+ formats = sun8i_vi_layer_de3_formats;
+ format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
+ } else {
+ formats = sun8i_vi_layer_de3_no_yuv_formats;
+ format_count = ARRAY_SIZE(sun8i_vi_layer_de3_no_yuv_formats);
+ }
} else {
formats = sun8i_vi_layer_formats;
format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
--
2.54.0
^ permalink raw reply related
* [PATCH v2 7/8] dt-bindings: display: allwinner: Split H616 DE33 layer reg space
From: Jernej Skrabec @ 2026-05-09 19:00 UTC (permalink / raw)
To: wens, samuel
Cc: mripard, maarten.lankhorst, tzimmermann, airlied, simona, robh,
krzk+dt, conor+dt, mturquette, sboyd, dri-devel, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk,
jernej.skrabec
In-Reply-To: <20260509190015.79086-1-jernej.skrabec@siol.net>
From: Jernej Skrabec <jernej.skrabec@gmail.com>
As it turns out, current H616 DE33 binding was written based on
incomplete understanding of DE33 design. Namely, planes are shared
resource and not tied to specific mixer, which was the case for previous
generations of Display Engine (DE3 and earlier).
This means that current DE33 binding doesn't properly reflect HW and
using it would mean that second mixer (used for second display output)
can't be supported.
Remove layer register space, which will be represented with additional
node, and replace it with phandle, which will point to that new, shared
node. That way, all mixers can share same layers.
There is no user of this binding yet, so changes can be made safely,
without breaking any backward compatibility.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
Changes in v1:
- update commit subject
- reword commit message
.../display/allwinner,sun8i-a83t-de2-mixer.yaml | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
index cbd18fd83e52..064e4ca7e419 100644
--- a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
+++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml
@@ -46,6 +46,10 @@ properties:
resets:
maxItems: 1
+ allwinner,planes:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Phandle of Display Engine 3.3 planes node
+
ports:
$ref: /schemas/graph.yaml#/properties/ports
@@ -74,22 +78,22 @@ allOf:
properties:
reg:
description: |
- Registers for controlling individual layers of the display
- engine (layers), global control (top), and display blending
- control (display). Names are from Allwinner BSP kernel.
- maxItems: 3
+ Registers for display blending control (display) and global
+ control (top). Names are from Allwinner BSP kernel.
+ maxItems: 2
reg-names:
items:
- - const: layers
- - const: top
- const: display
+ - const: top
required:
- reg-names
+ - allwinner,planes
else:
properties:
reg:
maxItems: 1
+ allwinner,planes: false
required:
- compatible
--
2.54.0
^ permalink raw reply related
* [PATCH v2 3/8] drm/sun4i: Add support for DE33 CSC
From: Jernej Skrabec @ 2026-05-09 19:00 UTC (permalink / raw)
To: wens, samuel
Cc: mripard, maarten.lankhorst, tzimmermann, airlied, simona, robh,
krzk+dt, conor+dt, mturquette, sboyd, dri-devel, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk,
jernej.skrabec, Chen-Yu Tsai
In-Reply-To: <20260509190015.79086-1-jernej.skrabec@siol.net>
From: Jernej Skrabec <jernej.skrabec@gmail.com>
DE33 has channel CSC units (for each plane separately) so pipeline can
be configured to output in desired colorspace.
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
Changes from v1:
- collected tag
drivers/gpu/drm/sun4i/sun8i_csc.c | 71 +++++++++++++++++++++++++++++++
drivers/gpu/drm/sun4i/sun8i_csc.h | 5 +++
2 files changed, 76 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c
index ce81c12f511d..70fc9b017d17 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -205,6 +205,72 @@ static void sun8i_de3_ccsc_setup(struct regmap *map, int layer,
mask, val);
}
+/* extract constant from high word and invert sign if necessary */
+static u32 sun8i_de33_ccsc_get_constant(u32 value)
+{
+ value >>= 16;
+
+ if (value & BIT(15))
+ return 0x400 - (value & 0x3ff);
+
+ return value;
+}
+
+static void sun8i_de33_convert_table(const u32 *src, u32 *dst)
+{
+ dst[0] = sun8i_de33_ccsc_get_constant(src[3]);
+ dst[1] = sun8i_de33_ccsc_get_constant(src[7]);
+ dst[2] = sun8i_de33_ccsc_get_constant(src[11]);
+ memcpy(&dst[3], src, sizeof(u32) * 12);
+ dst[6] &= 0xffff;
+ dst[10] &= 0xffff;
+ dst[14] &= 0xffff;
+}
+
+static void sun8i_de33_ccsc_setup(struct regmap *map, int layer,
+ enum sun8i_csc_mode mode,
+ enum drm_color_encoding encoding,
+ enum drm_color_range range)
+{
+ u32 addr, val, base, csc[15];
+ const u32 *table;
+ int i;
+
+ table = yuv2rgb_de3[range][encoding];
+ base = DE33_CCSC_BASE + layer * DE33_CH_SIZE;
+
+ switch (mode) {
+ case SUN8I_CSC_MODE_OFF:
+ val = 0;
+ break;
+ case SUN8I_CSC_MODE_YUV2RGB:
+ val = SUN8I_CSC_CTRL_EN;
+ sun8i_de33_convert_table(table, csc);
+ regmap_bulk_write(map, SUN50I_CSC_COEFF(base, 0), csc, 15);
+ break;
+ case SUN8I_CSC_MODE_YVU2RGB:
+ val = SUN8I_CSC_CTRL_EN;
+ sun8i_de33_convert_table(table, csc);
+ for (i = 0; i < 15; i++) {
+ addr = SUN50I_CSC_COEFF(base, i);
+ if (i > 3) {
+ if (((i - 3) & 3) == 1)
+ addr = SUN50I_CSC_COEFF(base, i + 1);
+ else if (((i - 3) & 3) == 2)
+ addr = SUN50I_CSC_COEFF(base, i - 1);
+ }
+ regmap_write(map, addr, csc[i]);
+ }
+ break;
+ default:
+ val = 0;
+ DRM_WARN("Wrong CSC mode specified.\n");
+ return;
+ }
+
+ regmap_write(map, SUN8I_CSC_CTRL(base), val);
+}
+
static u32 sun8i_csc_get_mode(struct drm_plane_state *state)
{
const struct drm_format_info *format;
@@ -238,6 +304,11 @@ void sun8i_csc_config(struct sun8i_layer *layer,
mode, state->color_encoding,
state->color_range);
return;
+ } else if (layer->cfg->de_type == SUN8I_MIXER_DE33) {
+ sun8i_de33_ccsc_setup(layer->regs, layer->channel,
+ mode, state->color_encoding,
+ state->color_range);
+ return;
}
base = ccsc_base[layer->cfg->ccsc][layer->channel];
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h
index 2a4b79599610..d2ba5f8611aa 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.h
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.h
@@ -18,9 +18,14 @@ struct sun8i_layer;
#define CCSC10_OFFSET 0xA0000
#define CCSC11_OFFSET 0xF0000
+#define DE33_CCSC_BASE 0x800
+
#define SUN8I_CSC_CTRL(base) ((base) + 0x0)
#define SUN8I_CSC_COEFF(base, i) ((base) + 0x10 + 4 * (i))
+#define SUN50I_CSC_COEFF(base, i) ((base) + 0x04 + 4 * (i))
+#define SUN50I_CSC_ALPHA(base) ((base) + 0x40)
+
#define SUN8I_CSC_CTRL_EN BIT(0)
void sun8i_csc_config(struct sun8i_layer *layer,
--
2.54.0
^ permalink raw reply related
* [PATCH v2 5/8] dt-bindings: display: allwinner: Add DE33 planes
From: Jernej Skrabec @ 2026-05-09 19:00 UTC (permalink / raw)
To: wens, samuel
Cc: mripard, maarten.lankhorst, tzimmermann, airlied, simona, robh,
krzk+dt, conor+dt, mturquette, sboyd, dri-devel, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk,
jernej.skrabec
In-Reply-To: <20260509190015.79086-1-jernej.skrabec@siol.net>
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Allwinner Display Engine 3.3 contains planes, which are shared resources
between all mixers present in SoC. They can be assigned to specific
mixer by using registers which reside in display clocks MMIO.
Add a binding for them.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
Changes from v1:
- dropped | for description
- better description for allwinner,plane-mapping
.../allwinner,sun50i-h616-de33-planes.yaml | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/allwinner,sun50i-h616-de33-planes.yaml
diff --git a/Documentation/devicetree/bindings/display/allwinner,sun50i-h616-de33-planes.yaml b/Documentation/devicetree/bindings/display/allwinner,sun50i-h616-de33-planes.yaml
new file mode 100644
index 000000000000..0a07cb8a5f79
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/allwinner,sun50i-h616-de33-planes.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/allwinner,sun50i-h616-de33-planes.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner H616 Display Engine 3.3 planes
+
+maintainers:
+ - Jernej Skrabec <jernej.skrabec@gmail.com>
+
+description:
+ Display Engine 3.3 planes are independent of mixers, contrary to
+ previous generations of Display Engine. Planes can be assigned to
+ mixers independently and even dynamically during runtime.
+
+properties:
+ compatible:
+ enum:
+ - allwinner,sun50i-h616-de33-planes
+
+ reg:
+ maxItems: 1
+
+ allwinner,plane-mapping:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle of the device responsible for assigning (mapping) planes
+ between mixers.
+
+required:
+ - compatible
+ - reg
+ - allwinner,plane-mapping
+
+additionalProperties: false
+
+examples:
+ - |
+ planes: planes@100000 {
+ compatible = "allwinner,sun50i-h616-de33-planes";
+ reg = <0x100000 0x180000>;
+ allwinner,plane-mapping = <&display_clocks>;
+ };
+
+...
--
2.54.0
^ permalink raw reply related
* [PATCH v2 2/8] clk: sunxi-ng: de2: Export register regmap for DE33
From: Jernej Skrabec @ 2026-05-09 19:00 UTC (permalink / raw)
To: wens, samuel
Cc: mripard, maarten.lankhorst, tzimmermann, airlied, simona, robh,
krzk+dt, conor+dt, mturquette, sboyd, dri-devel, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk,
jernej.skrabec
In-Reply-To: <20260509190015.79086-1-jernej.skrabec@siol.net>
From: Jernej Skrabec <jernej.skrabec@gmail.com>
DE33 clock pre-set plane mapping, which is not something that we want
from clock driver. Export registers instead, so DRM driver can set them
properly.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
Changes from v1:
- used access tables instead of functions
- used macro for max register
- used dev_of_node() instead of open coding
drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 48 ++++++++++++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
index 6ec6c0dc4c26..26d8abd5fac6 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
@@ -6,9 +6,11 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include "ccu_common.h"
@@ -283,6 +285,36 @@ static const struct sunxi_ccu_desc sun50i_h616_de33_clk_desc = {
.num_resets = ARRAY_SIZE(sun50i_h616_de33_resets),
};
+/*
+ * Add a regmap for the DE33 plane driver to access plane
+ * mapping registers.
+ * Only these registers are allowed to be written, to prevent
+ * overriding clock and reset configuration.
+ */
+
+#define SUN50I_DE33_CHN2CORE_REG 0x24
+#define SUN50I_DE33_PORT12CHN_REG 0x2c
+
+static const struct regmap_range sun8i_de2_ccu_regmap_accessible_ranges[] = {
+ regmap_reg_range(SUN50I_DE33_CHN2CORE_REG, SUN50I_DE33_PORT12CHN_REG),
+};
+
+static const struct regmap_access_table sun8i_de2_ccu_regmap_accessible_table = {
+ .yes_ranges = sun8i_de2_ccu_regmap_accessible_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sun8i_de2_ccu_regmap_accessible_ranges),
+};
+
+static const struct regmap_config sun8i_de2_ccu_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SUN50I_DE33_PORT12CHN_REG,
+
+ /* other devices have no business accessing other registers */
+ .wr_table = &sun8i_de2_ccu_regmap_accessible_table,
+ .rd_table = &sun8i_de2_ccu_regmap_accessible_table,
+};
+
static int sunxi_de2_clk_probe(struct platform_device *pdev)
{
struct clk *bus_clk, *mod_clk;
@@ -336,13 +368,23 @@ static int sunxi_de2_clk_probe(struct platform_device *pdev)
}
/*
- * The DE33 requires these additional (unknown) registers set
+ * The DE33 requires these additional plane mapping registers set
* during initialisation.
*/
if (of_device_is_compatible(pdev->dev.of_node,
"allwinner,sun50i-h616-de33-clk")) {
- writel(0, reg + 0x24);
- writel(0x0000a980, reg + 0x28);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, reg,
+ &sun8i_de2_ccu_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto err_assert_reset;
+ }
+
+ ret = of_syscon_register_regmap(dev_of_node(&pdev->dev), regmap);
+ if (ret)
+ goto err_assert_reset;
}
ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] clk: keystone: sci-clk: fix application of sizeof to pointer
From: Stepan Ionichev @ 2026-05-09 11:34 UTC (permalink / raw)
To: nm
Cc: sboyd, mturquette, afd, linux-clk, linux-arm-kernel, linux-kernel,
sozdayvek
In-Reply-To: <20260508152321.3683799-1-nm@ti.com>
In sci_clk_get(), 'clk' is declared as 'struct sci_clk **', so
sizeof(clk) is sizeof(struct sci_clk **) which is the size of a
pointer rather than the size of an array element. provider->clocks
is an array of 'struct sci_clk *', so the canonical size argument
to bsearch() is sizeof(*clk) (i.e. sizeof(struct sci_clk *)).
The two values are equal on every supported architecture, so this
is correctness/idiom, not a runtime fix, but the new form matches
the rest of the bsearch() callers in the tree and silences the
Coccinelle warning the script flagged.
Reviewed-by: Stepan Ionichev <sozdayvek@gmail.com>
Stepan
^ permalink raw reply
* [PATCH v2] dt-bindings: iio: adc: Convert xilinx-xadc bindings to YAML schema
From: Pramod Maurya @ 2026-05-09 18:07 UTC (permalink / raw)
To: jic23
Cc: pramod.nexgen, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michal Simek,
Lars-Peter Clausen, linux-iio, devicetree, linux-arm-kernel,
linux-kernel
Convert the Xilinx XADC and UltraScale System Monitor device tree binding
from the legacy plain-text format to a YAML schema, enabling automated
validation with dt-schema.
The new binding covers the same hardware and compatible strings:
- xlnx,zynq-xadc-1.00.a (ZYNQ hardmacro)
- xlnx,axi-xadc-1.00.a (AXI softmacro)
- xlnx,system-management-wiz-1.3 (UltraScale System Management Wizard)
Signed-off-by: Pramod Maurya <pramod.nexgen@gmail.com>
---
Changes in v2:
- Fix patternProperties regex to use lowercase hex unit addresses
(channel@a through channel@f) instead of decimal; correct range
is now "^channel@([0-9a-f]|10)$"
- Add allOf/if/then conditional to enforce xlnx,external-mux-channel
is required when xlnx,external-mux is "single" or "dual"
.../bindings/iio/adc/xilinx-xadc.txt | 141 ------------
.../bindings/iio/adc/xlnx,xadc.yaml | 204 ++++++++++++++++++
MAINTAINERS | 7 +
3 files changed, 211 insertions(+), 141 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
create mode 100644 Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt b/Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
deleted file mode 100644
index f42e18078376..000000000000
--- a/Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
+++ /dev/null
@@ -1,141 +0,0 @@
-Xilinx XADC device driver
-
-This binding document describes the bindings for the Xilinx 7 Series XADC as well
-as the UltraScale/UltraScale+ System Monitor.
-
-The Xilinx XADC is an ADC that can be found in the Series 7 FPGAs from Xilinx.
-The XADC has a DRP interface for communication. Currently two different
-frontends for the DRP interface exist. One that is only available on the ZYNQ
-family as a hardmacro in the SoC portion of the ZYNQ. The other one is available
-on all series 7 platforms and is a softmacro with a AXI interface. This binding
-document describes the bindings for both of them since the bindings are very
-similar.
-
-The Xilinx System Monitor is an ADC that is found in the UltraScale and
-UltraScale+ FPGAs from Xilinx. The System Monitor provides a DRP interface for
-communication. Xilinx provides a standard IP core that can be used to access the
-System Monitor through an AXI interface in the FPGA fabric. This IP core is
-called the Xilinx System Management Wizard. This document describes the bindings
-for this IP.
-
-Required properties:
- - compatible: Should be one of
- * "xlnx,zynq-xadc-1.00.a": When using the ZYNQ device
- configuration interface to interface to the XADC hardmacro.
- * "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
- interface to the XADC hardmacro.
- * "xlnx,system-management-wiz-1.3": When using the
- Xilinx System Management Wizard fabric IP core to access the
- UltraScale and UltraScale+ System Monitor.
- - reg: Address and length of the register set for the device
- - interrupts: Interrupt for the XADC control interface.
- - clocks: When using the ZYNQ this must be the ZYNQ PCAP clock,
- when using the axi-xadc or the axi-system-management-wizard this must be
- the clock that provides the clock to the AXI bus interface of the core.
-
-Optional properties:
- - xlnx,external-mux:
- * "none": No external multiplexer is used, this is the default
- if the property is omitted.
- * "single": External multiplexer mode is used with one
- multiplexer.
- * "dual": External multiplexer mode is used with two
- multiplexers for simultaneous sampling.
- - xlnx,external-mux-channel: Configures which pair of pins is used to
- sample data in external mux mode.
- Valid values for single external multiplexer mode are:
- 0: VP/VN
- 1: VAUXP[0]/VAUXN[0]
- 2: VAUXP[1]/VAUXN[1]
- ...
- 16: VAUXP[15]/VAUXN[15]
- Valid values for dual external multiplexer mode are:
- 1: VAUXP[0]/VAUXN[0] - VAUXP[8]/VAUXN[8]
- 2: VAUXP[1]/VAUXN[1] - VAUXP[9]/VAUXN[9]
- ...
- 8: VAUXP[7]/VAUXN[7] - VAUXP[15]/VAUXN[15]
-
- This property needs to be present if the device is configured for
- external multiplexer mode (either single or dual). If the device is
- not using external multiplexer mode the property is ignored.
- - xnlx,channels: List of external channels that are connected to the ADC
- Required properties:
- * #address-cells: Should be 1.
- * #size-cells: Should be 0.
-
- The child nodes of this node represent the external channels which are
- connected to the ADC. If the property is no present no external
- channels will be assumed to be connected.
-
- Each child node represents one channel and has the following
- properties:
- Required properties:
- * reg: Pair of pins the channel is connected to.
- 0: VP/VN
- 1: VAUXP[0]/VAUXN[0]
- 2: VAUXP[1]/VAUXN[1]
- ...
- 16: VAUXP[15]/VAUXN[15]
- Note each channel number should only be used at most
- once.
- Optional properties:
- * xlnx,bipolar: If set the channel is used in bipolar
- mode.
-
-
-Examples:
- xadc@f8007100 {
- compatible = "xlnx,zynq-xadc-1.00.a";
- reg = <0xf8007100 0x20>;
- interrupts = <0 7 4>;
- interrupt-parent = <&gic>;
- clocks = <&pcap_clk>;
-
- xlnx,channels {
- #address-cells = <1>;
- #size-cells = <0>;
- channel@0 {
- reg = <0>;
- };
- channel@1 {
- reg = <1>;
- };
- channel@8 {
- reg = <8>;
- };
- };
- };
-
- xadc@43200000 {
- compatible = "xlnx,axi-xadc-1.00.a";
- reg = <0x43200000 0x1000>;
- interrupts = <0 53 4>;
- interrupt-parent = <&gic>;
- clocks = <&fpga1_clk>;
-
- xlnx,channels {
- #address-cells = <1>;
- #size-cells = <0>;
- channel@0 {
- reg = <0>;
- xlnx,bipolar;
- };
- };
- };
-
- adc@80000000 {
- compatible = "xlnx,system-management-wiz-1.3";
- reg = <0x80000000 0x1000>;
- interrupts = <0 81 4>;
- interrupt-parent = <&gic>;
- clocks = <&fpga1_clk>;
-
- xlnx,channels {
- #address-cells = <1>;
- #size-cells = <0>;
- channel@0 {
- reg = <0>;
- xlnx,bipolar;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml b/Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
new file mode 100644
index 000000000000..cfe753e3e8d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/xlnx,xadc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xilinx XADC and UltraScale System Monitor
+
+maintainers:
+ - Lars-Peter Clausen <lars@metafoo.de>
+
+description: |
+ The Xilinx XADC is an ADC found in the Series 7 FPGAs. It has a DRP
+ (Dynamic Reconfiguration Port) interface for communication. Two different
+ frontends for the DRP interface are supported:
+
+ - ZYNQ hardmacro: available only on the ZYNQ family as a hardmacro in
+ the SoC portion of the ZYNQ device.
+ - AXI softmacro: available on all Series 7 platforms as a softmacro
+ with an AXI interface (PG019).
+
+ The Xilinx System Monitor is an ADC found in UltraScale and UltraScale+
+ FPGAs. It is accessed through the Xilinx System Management Wizard IP core
+ via an AXI interface in the FPGA fabric.
+
+properties:
+ compatible:
+ enum:
+ - xlnx,zynq-xadc-1.00.a
+ - xlnx,axi-xadc-1.00.a
+ - xlnx,system-management-wiz-1.3
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ description: |
+ When using the ZYNQ this must be the ZYNQ PCAP clock.
+ When using the axi-xadc or system-management-wiz this must be
+ the clock that provides the clock to the AXI bus interface.
+ maxItems: 1
+
+ xlnx,external-mux:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: |
+ Selects the external multiplexer mode.
+ enum:
+ - none
+ - single
+ - dual
+
+ xlnx,external-mux-channel:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Configures which pair of pins is used to sample data in external
+ multiplexer mode. This property is required when the device is
+ configured for external multiplexer mode.
+
+ Valid values for single external multiplexer mode:
+ 0: VP/VN
+ 1-16: VAUXP[0]/VAUXN[0] through VAUXP[15]/VAUXN[15]
+
+ Valid values for dual external multiplexer mode:
+ 1: VAUXP[0]/VAUXN[0] - VAUXP[8]/VAUXN[8]
+ 2: VAUXP[1]/VAUXN[1] - VAUXP[9]/VAUXN[9]
+ ...
+ 8: VAUXP[7]/VAUXN[7] - VAUXP[15]/VAUXN[15]
+ minimum: 0
+ maximum: 16
+
+ xlnx,channels:
+ type: object
+ description:
+ List of external channels connected to the ADC. If this property is
+ absent, no external channels are assumed to be connected.
+
+ properties:
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ patternProperties:
+ "^channel@([0-9a-f]|10)$":
+ type: object
+ description:
+ Represents an external channel connected to the ADC.
+
+ properties:
+ reg:
+ description: |
+ Pair of pins the channel is connected to.
+ 0: VP/VN
+ 1: VAUXP[0]/VAUXN[0]
+ 2: VAUXP[1]/VAUXN[1]
+ ...
+ 16: VAUXP[15]/VAUXN[15]
+ minimum: 0
+ maximum: 16
+
+ xlnx,bipolar:
+ $ref: /schemas/types.yaml#/definitions/flag
+ type: boolean
+ description:
+ If set, the channel is used in bipolar mode.
+
+ required:
+ - reg
+
+ additionalProperties: false
+
+ required:
+ - '#address-cells'
+ - '#size-cells'
+
+ additionalProperties: false
+
+allOf:
+ - if:
+ properties:
+ xlnx,external-mux:
+ enum:
+ - single
+ - dual
+ required:
+ - xlnx,external-mux
+ then:
+ required:
+ - xlnx,external-mux-channel
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ /* ZYNQ hardmacro example */
+ adc@f8007100 {
+ compatible = "xlnx,zynq-xadc-1.00.a";
+ reg = <0xf8007100 0x20>;
+ interrupts = <0 7 4>;
+ interrupt-parent = <&gic>;
+ clocks = <&pcap_clk>;
+
+ xlnx,channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ };
+ channel@1 {
+ reg = <1>;
+ };
+ channel@8 {
+ reg = <8>;
+ };
+ };
+ };
+
+ - |
+ /* AXI softmacro example */
+ adc@43200000 {
+ compatible = "xlnx,axi-xadc-1.00.a";
+ reg = <0x43200000 0x1000>;
+ interrupts = <0 53 4>;
+ interrupt-parent = <&gic>;
+ clocks = <&fpga1_clk>;
+
+ xlnx,channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ xlnx,bipolar;
+ };
+ };
+ };
+
+ - |
+ /* UltraScale System Management Wizard example */
+ adc@80000000 {
+ compatible = "xlnx,system-management-wiz-1.3";
+ reg = <0x80000000 0x1000>;
+ interrupts = <0 81 4>;
+ interrupt-parent = <&gic>;
+ clocks = <&fpga1_clk>;
+
+ xlnx,channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ xlnx,bipolar;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..9b107057ad8c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29226,6 +29226,13 @@ F: Documentation/devicetree/bindings/watchdog/xlnx,xps-timebase-wdt.yaml
F: drivers/watchdog/of_xilinx_wdt.c
F: drivers/watchdog/xilinx_wwdt.c
+XILINX XADC DRIVER
+M: Lars-Peter Clausen <lars@metafoo.de>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
+F: drivers/iio/adc/xilinx-xadc*
+
XILINX XDMA DRIVER
M: Lizhi Hou <lizhi.hou@amd.com>
M: Brian Xu <brian.xu@amd.com>
--
2.52.0
^ permalink raw reply related
* [PATCH] clk: mstar: msc313-mpll: fix one-element shadow array overrun
From: Stepan Ionichev @ 2026-05-09 10:42 UTC (permalink / raw)
To: daniel
Cc: romain.perier, mturquette, sboyd, linux-arm-kernel, linux-clk,
linux-kernel, Stepan Ionichev
msc313_mpll_probe() defines
#define NUMOUTPUTS (ARRAY_SIZE(output_dividers) + 1)
and uses NUMOUTPUTS for the clock provider's clk_hw count
(clk_data->num = NUMOUTPUTS) and writes that many entries:
mpll->clk_data->hws[0] = &mpll->clk_hw; /* parent */
for (i = 0; i < ARRAY_SIZE(output_dividers); i++) /* dividers */
mpll->clk_data->hws[i + 1] = divhw;
So the function legitimately needs NUMOUTPUTS slots in the
flexible 'hws' array. However the array is allocated for only
ARRAY_SIZE(output_dividers) (== NUMOUTPUTS - 1) slots:
mpll->clk_data = devm_kzalloc(dev, struct_size(mpll->clk_data, hws,
ARRAY_SIZE(output_dividers)), GFP_KERNEL);
The last loop iteration therefore writes one element past the
allocation, and clk_data->num advertises a slot that does not
exist to of_clk_hw_onecell_get().
smatch flags the underflow:
drivers/clk/mstar/clk-msc313-mpll.c:134 msc313_mpll_probe()
error: buffer overflow 'mpll->clk_data->hws' 7 <= 7
Use NUMOUTPUTS in struct_size() so the allocation matches the
declared count and the loop's last write.
Signed-off-by: Stepan Ionichev <sozdayvek@gmail.com>
---
drivers/clk/mstar/clk-msc313-mpll.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clk/mstar/clk-msc313-mpll.c b/drivers/clk/mstar/clk-msc313-mpll.c
index 61beb4e87..71bedb7e9 100644
--- a/drivers/clk/mstar/clk-msc313-mpll.c
+++ b/drivers/clk/mstar/clk-msc313-mpll.c
@@ -105,7 +105,7 @@ static int msc313_mpll_probe(struct platform_device *pdev)
return PTR_ERR(mpll->loop_div_second);
mpll->clk_data = devm_kzalloc(dev, struct_size(mpll->clk_data, hws,
- ARRAY_SIZE(output_dividers)), GFP_KERNEL);
+ NUMOUTPUTS), GFP_KERNEL);
if (!mpll->clk_data)
return -ENOMEM;
--
2.43.0
^ permalink raw reply related
* Re: Regression in split ARM MMIO timer driver
From: Marc Zyngier @ 2026-05-09 17:58 UTC (permalink / raw)
To: Sudeep Holla, Jack Matthews
Cc: Mark Rutland, linux-arm-kernel, regressions, linux-kernel,
linux-arm-msm
In-Reply-To: <20260509-ruddy-dragonfly-of-poetry-45e9f1@sudeepholla>
On Sat, 09 May 2026 13:19:56 +0100,
Sudeep Holla <sudeep.holla@kernel.org> wrote:
>
> On Fri, May 08, 2026 at 03:48:14PM -0400, Jack Matthews wrote:
> > Hello,
> >
> > I am working on mainlining an old chip, Qualcomm's MDM9625 modem.
> > I had previously booted 6.17-rc3 before putting this project to the side,
> > but when I restarted work on 7.0 I was unable to boot.
> > I have bisected this to commit 0f67b56d84b4c49adfd61f19f81f84ec613ab51a
> > (https://lore.kernel.org/all/20250814154622.10193-4-maz@kernel.org/) and
> > reverting this commit makes the device boot successfully. Unfortunately I do
> > not have access to low level debugging such as UART so I have not been able
> > to pinpoint exactly what is missing.
> > My changes for this chip are all available here in case it is an issue of my
> > own doing: https://github.com/jackmthws/linux/commits/mdm9625-latest/.
>
> Looking briefly into the DTS file, I couldn't find the sysreg based
> arch timer node in the DT. It could be just an overlook unless there
> is some issue with it that it's not added. After the above mentioned
> commit, the MMIO timer gets initialised bit late in the boot and
> could be the reason for boot failure. Unless you have intentionally
> not added it, I would suggest to add it and try.
Ah, that's a good point. Not having per-CPU timers is not going to
fly, I'm afraid. The MMIO timer will kick in very late, and we
probably need to schedule threads way before that.
This looks to be a single Cortex-A5 however, not an A5MP, meaning it
does not have the TWD, and relies on a global timer, which is a
terrible thing to have. Not to mention that MMIO accesses on the
counter is going to be even worse.
Anyway, if that's indeed the case, we'll have to get creative to
support this sort of horror (not going to butcher the driver to make
this work).
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply
* Re: [PATCH v14 1/5] dt-bindings: vendor-prefixes: Add Verisilicon
From: Diederik de Haas @ 2026-05-09 17:56 UTC (permalink / raw)
To: Benjamin Gaignard, joro, will, robin.murphy, krzk+dt, conor+dt,
heiko
Cc: iommu, devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
kernel, Conor Dooley
In-Reply-To: <20260415072349.44237-2-benjamin.gaignard@collabora.com>
On Wed Apr 15, 2026 at 9:23 AM CEST, Benjamin Gaignard wrote:
> Verisilicon Microelectronics is a company based in Shanghai, China,
> developping hardware blocks for SoC.
>
> https://verisilicon.com/
>
> Add their name to the list of vendors.
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> index ee7fd3cfe203..ebd9072300a8 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> @@ -1761,6 +1761,8 @@ patternProperties:
> description: Variscite Ltd.
> "^vdl,.*":
> description: Van der Laan b.v.
> + "^verisilicon,.*":
> + description: VeriSilicon Microelectronics
> "^vertexcom,.*":
> description: Vertexcom Technologies, Inc.
> "^via,.*":
FTR: Another version of this patch is already present in Linus' tree:
c131d78840d7 ("dt-bindings: vendor-prefixes: add verisilicon")
^ permalink raw reply
* [PATCH] spi: mtk-snfi: Fix resource leak in mtk_snand_read_page_cache()
From: Felix Gu @ 2026-05-09 17:55 UTC (permalink / raw)
To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
Chuanhong Guo
Cc: linux-spi, linux-kernel, linux-arm-kernel, linux-mediatek,
Felix Gu
When DMA read times out in mtk_snand_read_page_cache(), the original code
erroneously jumped to cleanup label which skips DMA unmapping and ECC
disable, causing a resource leak.
Fixes: 764f1b748164 ("spi: add driver for MTK SPI NAND Flash Interface")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
drivers/spi/spi-mtk-snfi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c
index e616e6800e92..6e96e50fedad 100644
--- a/drivers/spi/spi-mtk-snfi.c
+++ b/drivers/spi/spi-mtk-snfi.c
@@ -961,7 +961,7 @@ static int mtk_snand_read_page_cache(struct mtk_snand *snf,
&snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) {
dev_err(snf->dev, "DMA timed out for reading from cache.\n");
ret = -ETIMEDOUT;
- goto cleanup;
+ goto cleanup2;
}
// Wait for BUS_SEC_CNTR returning expected value
---
base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83
change-id: 20260509-snfi-c8d170513987
Best regards,
--
Felix Gu <ustc.gu@gmail.com>
^ permalink raw reply related
* [PATCH 2/2] arm64: dts: Add Airoha AN7583 SoC and AN7583 Evaluation Board
From: Lorenzo Bianconi @ 2026-05-09 17:38 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Felix Fietkau,
John Crispin, Matthias Brugger, AngeloGioacchino Del Regno
Cc: devicetree, linux-arm-kernel, linux-mediatek, Lorenzo Bianconi
In-Reply-To: <20260509-airoha-7583-v1-0-e5ad085b258e@kernel.org>
Introduce the Airoha AN7583 SoC's dtsi and the Airoha AN7583 Evaluation
Board's dts file.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
arch/arm64/boot/dts/airoha/Makefile | 2 +-
arch/arm64/boot/dts/airoha/an7583-evb.dts | 23 +++++
arch/arm64/boot/dts/airoha/an7583.dtsi | 137 ++++++++++++++++++++++++++++++
3 files changed, 161 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/airoha/Makefile b/arch/arm64/boot/dts/airoha/Makefile
index ebea112ce1d7..6027978a35c2 100644
--- a/arch/arm64/boot/dts/airoha/Makefile
+++ b/arch/arm64/boot/dts/airoha/Makefile
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
-dtb-$(CONFIG_ARCH_AIROHA) += en7581-evb.dtb
+dtb-$(CONFIG_ARCH_AIROHA) += en7581-evb.dtb an7583-evb.dtb
diff --git a/arch/arm64/boot/dts/airoha/an7583-evb.dts b/arch/arm64/boot/dts/airoha/an7583-evb.dts
new file mode 100644
index 000000000000..76b3d94434f6
--- /dev/null
+++ b/arch/arm64/boot/dts/airoha/an7583-evb.dts
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/dts-v1/;
+
+#include "an7583.dtsi"
+
+/ {
+ model = "Airoha AN7583 Evaluation Board";
+ compatible = "airoha,an7583-evb", "airoha,an7583";
+
+ aliases {
+ serial0 = &uart1;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200 earlycon";
+ stdout-path = "serial0:115200n8";
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x0 0x80000000 0x2 0x00000000>;
+ };
+};
diff --git a/arch/arm64/boot/dts/airoha/an7583.dtsi b/arch/arm64/boot/dts/airoha/an7583.dtsi
new file mode 100644
index 000000000000..64b2c7533aff
--- /dev/null
+++ b/arch/arm64/boot/dts/airoha/an7583.dtsi
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ atf@80000000 {
+ no-map;
+ reg = <0x0 0x80000000 0x0 0x200000>;
+ };
+ };
+
+ psci {
+ compatible = "arm,psci-1.0";
+ method = "smc";
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu-map {
+ cluster0 {
+ core0 {
+ cpu = <&cpu0>;
+ };
+
+ core1 {
+ cpu = <&cpu1>;
+ };
+ };
+ };
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x0>;
+ enable-method = "psci";
+ next-level-cache = <&l2>;
+ #cooling-cells = <2>;
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1>;
+ enable-method = "psci";
+ next-level-cache = <&l2>;
+ #cooling-cells = <2>;
+ };
+
+ l2: l2-cache {
+ compatible = "cache";
+ cache-size = <0x80000>;
+ cache-line-size = <64>;
+ cache-level = <2>;
+ cache-unified;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ };
+
+ clk25m: clock-25000000 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "clkxtal";
+ };
+
+ i2c_clock: clock-20000000 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <20000000>;
+ clock-output-names = "i2c_clock";
+ };
+
+ sys_hclk: clock-100000000 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "sys_hclk";
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ gic: interrupt-controller@9000000 {
+ compatible = "arm,gic-v3";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x0 0x09000000 0x0 0x20000>,
+ <0x0 0x09080000 0x0 0x80000>,
+ <0x0 0x09400000 0x0 0x2000>,
+ <0x0 0x09500000 0x0 0x2000>,
+ <0x0 0x09600000 0x0 0x20000>;
+ interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_LOW>;
+ };
+
+ uart1: serial@1fbf0000 {
+ compatible = "ns16550";
+ reg = <0x0 0x1fbf0000 0x0 0x30>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+ interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <1843200>;
+ };
+
+ watchdog@1fbf0100 {
+ compatible = "airoha,en7581-wdt";
+ reg = <0x0 0x1fbf0100 0x0 0x38>;
+
+ clocks = <&sys_hclk>;
+ clock-names = "bus";
+ };
+ };
+};
--
2.54.0
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: arm64: dts: airoha: Add an7583 entry
From: Lorenzo Bianconi @ 2026-05-09 17:38 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Felix Fietkau,
John Crispin, Matthias Brugger, AngeloGioacchino Del Regno
Cc: devicetree, linux-arm-kernel, linux-mediatek, Lorenzo Bianconi
In-Reply-To: <20260509-airoha-7583-v1-0-e5ad085b258e@kernel.org>
Introduce Airoha AN7583 entry in Airoha dts binding.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Documentation/devicetree/bindings/arm/airoha.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/airoha.yaml b/Documentation/devicetree/bindings/arm/airoha.yaml
index 7c38c08dbf3f..6506c64af4f3 100644
--- a/Documentation/devicetree/bindings/arm/airoha.yaml
+++ b/Documentation/devicetree/bindings/arm/airoha.yaml
@@ -26,6 +26,10 @@ properties:
- enum:
- airoha,en7581-evb
- const: airoha,en7581
+ - items:
+ - enum:
+ - airoha,an7583-evb
+ - const: airoha,an7583
additionalProperties: true
--
2.54.0
^ permalink raw reply related
* [PATCH 0/2] Introduce Airoha AN7583 SoC and AN7583 Evaluation Board
From: Lorenzo Bianconi @ 2026-05-09 17:38 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Felix Fietkau,
John Crispin, Matthias Brugger, AngeloGioacchino Del Regno
Cc: devicetree, linux-arm-kernel, linux-mediatek, Lorenzo Bianconi
Introduce the Airoha AN7583 SoC's dtsi and the Airoha AN7583 Evaluation
Board's dts file.
---
Lorenzo Bianconi (2):
dt-bindings: arm64: dts: airoha: Add an7583 entry
arm64: dts: Add Airoha AN7583 SoC and AN7583 Evaluation Board
Documentation/devicetree/bindings/arm/airoha.yaml | 4 +
arch/arm64/boot/dts/airoha/Makefile | 2 +-
arch/arm64/boot/dts/airoha/an7583-evb.dts | 23 ++++
arch/arm64/boot/dts/airoha/an7583.dtsi | 137 ++++++++++++++++++++++
4 files changed, 165 insertions(+), 1 deletion(-)
---
base-commit: e6490a169f6d5f5bdea7a2e8a673890d43afadc0
change-id: 20260509-airoha-7583-145246084016
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply
* Re: tpm: spi: do not call blocking ops when !TASK_RUNNING; during shutdown
From: Jarkko Sakkinen @ 2026-05-09 17:35 UTC (permalink / raw)
To: Stefan Wahren
Cc: Peter Huewe, Jason Gunthorpe, linux-arm-kernel, linux-integrity,
kernel, Frank Li, Sascha Hauer, imx@lists.linux.dev
In-Reply-To: <6964bec7-3dbb-453b-89ef-9b990217a8b9@gmx.net>
[-- Attachment #1: Type: text/plain, Size: 3767 bytes --]
On Mon, May 04, 2026 at 06:05:25PM +0200, Stefan Wahren wrote:
> Am 04.05.26 um 15:42 schrieb Jarkko Sakkinen:
> > Hi Stefan,
> >
> > On Mon, Apr 20, 2026 at 12:25:21PM +0200, Stefan Wahren wrote:
> > > Hi,
> > > we use a custom i.MX93 board, which based on Phytec Phycore i.MX93 with a
> > > TPM connected via SPI. If I enable CONFIG_DEBUG_ATOMIC_SLEEP=y in our kernel
> > > config with mainline kernel 6.18.23 and reboot our board, I will get the
> > > following warning:
> > Please provide results with the latest mainline kernel.
> >
> Here you are
>
> [ 69.085410] do not call blocking ops when !TASK_RUNNING; state=1 set at
> [<0000000090f4020f>] prepare_to_wait_event+0x54/0x14c
> [ 69.097076] WARNING: kernel/sched/core.c:9086 at __might_sleep+0x74/0x7c,
> CPU#0: systemd-shutdow/1
> [ 69.106026] Modules linked in: flexcan can_dev rtc_rv3028 mse102x
> phy_can_transceiver fuse autofs4
> [ 69.114991] CPU: 0 UID: 0 PID: 1 Comm: systemd-shutdow Not tainted
> 7.1.0-rc2-00005-g6d35786de281-dirty #45 PREEMPT
> [ 69.125405] Hardware name: chargebyte Charge SOM Evaluation Kit (DT)
> [ 69.131741] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS
> BTYPE=--)
> [ 69.138690] pc : __might_sleep+0x74/0x7c
> [ 69.142608] lr : __might_sleep+0x74/0x7c
> [ 69.146525] sp : ffff800081b7b710
> [ 69.149827] x29: ffff800081b7b710 x28: 0000000000000080 x27:
> ffff000002cafe80
> [ 69.156951] x26: ffff800081965000 x25: 0000000000000018 x24:
> 0000000000000000
> [ 69.164075] x23: ffff800081b7b8f7 x22: 0000000000000000 x21:
> 0000000000000080
> [ 69.171199] x20: 000000000000013c x19: ffff8000813e2880 x18:
> 0000000000000000
> [ 69.178323] x17: 003fffffffffffff x16: 000000000017d600 x15:
> ffff00003fda1a00
> [ 69.185447] x14: ffff000000132c00 x13: 00000000000002ce x12:
> 0000000000000000
> [ 69.192571] x11: 00000000000000c0 x10: 00000000000009e0 x9 :
> ffff800081b7b580
> [ 69.199695] x8 : ffff0000001335c0 x7 : ffff00003fda1280 x6 :
> 0000000000000000
> [ 69.206819] x5 : 000000001fffffff x4 : 0000000000000001 x3 :
> 0000000000000010
> [ 69.213943] x2 : 0000000000000000 x1 : 0000000000000000 x0 :
> ffff000000132b80
> [ 69.221068] Call trace:
> [ 69.223503] __might_sleep+0x74/0x7c (P)
> [ 69.227420] mutex_lock+0x24/0x80
> [ 69.230731] spi_bus_lock+0x20/0x50
> [ 69.234214] tpm_tis_spi_transfer_full+0x70/0x2c4
> [ 69.238912] tpm_tis_spi_read_bytes+0x3c/0x48
> [ 69.243263] tpm_tis_status+0x58/0xf8
> [ 69.246920] wait_for_tpm_stat_cond+0x30/0x90
> [ 69.251271] wait_for_tpm_stat+0x1cc/0x2e0
> [ 69.255361] tpm_tis_send_data+0xdc/0x35c
> [ 69.259365] tpm_tis_send_main+0x74/0x18c
> [ 69.263369] tpm_tis_send+0xd4/0x13c
> [ 69.266940] tpm_transmit+0xc4/0x3c4
> [ 69.270511] tpm_transmit_cmd+0x38/0xd4
> [ 69.274341] tpm2_shutdown+0x6c/0xa4
> [ 69.277912] tpm_class_shutdown+0x60/0x88
> [ 69.281916] device_shutdown+0x130/0x258
> [ 69.285833] kernel_restart+0x44/0xa4
> [ 69.289491] __do_sys_reboot+0x114/0x244
> [ 69.293408] __arm64_sys_reboot+0x24/0x30
> [ 69.297412] invoke_syscall+0x54/0x10c
> [ 69.301156] el0_svc_common.constprop.0+0x40/0xe0
> [ 69.305853] do_el0_svc+0x1c/0x28
> [ 69.309164] el0_svc+0x38/0x138
> [ 69.312301] el0t_64_sync_handler+0xa0/0xe4
> [ 69.316478] el0t_64_sync+0x198/0x19c
Thank you. This bug has been active since IRQs were enabled in tpm_tis_spi.
It's quite surprising that getting a report on it has taken so many years.
Anyway, my conclusion is that the bug report is totally legit.
"state=1" in the dump above refers to TASK_INTERRUPTIBLE, which unrolled
the root cause (I believe).
Here's an experimental fix that uses wake_woken(). It ensures that we
read status only while task is running.
BR, Jarkko
[-- Attachment #2: test.patch --]
[-- Type: text/x-diff, Size: 2780 bytes --]
From bf458dd675988a4fd78859a3e6a348071ecd2695 Mon Sep 17 00:00:00 2001
From: Jarkko Sakkinen <jarkko@kernel.org>
Date: Sat, 9 May 2026 19:55:30 +0300
Subject: [PATCH] tpm: tpm_tis_spi: Use wait_woken() in wait_for_tmp_stat()
wait_event_interruptible_timeout() evaluates its condition after setting
the current task state to TASK_INTERRUPTIBLE.
With CONFIG_DEBUG_ATOMIC_SLEEP this triggers a warning when the IRQ wait
path is used:
tpm_tis_status()
tpm_tis_spi_read_bytes()
tpm_tis_spi_transfer_full()
spi_bus_lock()
mutex_lock()
Address this with the following measures:
1. Call wait_tpm_stat_cond() only while tasking is running.
2. Use wait_woken() to wait for changes.
Cc: stable@vger.kernel.org # v4.19+
Cc: Linus Walleij <linusw@kernel.org>
Reported-by: Stefan Wahren <wahrenst@gmx.net>
Closes: https://lore.kernel.org/linux-integrity/6964bec7-3dbb-453b-89ef-9b990217a8b9@gmx.net/
Fixes: 1a339b658d9d ("tpm_tis_spi: Pass the SPI IRQ down to the driver")
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm_tis_core.c | 40 +++++++++++++++++++--------------
1 file changed, 23 insertions(+), 17 deletions(-)
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 21d79ad3b164..097ad0762895 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -66,8 +66,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
bool check_cancel)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
unsigned long stop;
- long rc;
u8 status;
bool canceled = false;
u8 sts_mask;
@@ -87,23 +87,29 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
/* process status changes with irq support */
if (sts_mask) {
ret = -ETIME;
-again:
- timeout = stop - jiffies;
- if ((long)timeout <= 0)
- return -ETIME;
- rc = wait_event_interruptible_timeout(*queue,
- wait_for_tpm_stat_cond(chip, sts_mask, check_cancel,
- &canceled),
- timeout);
- if (rc > 0) {
- if (canceled)
- return -ECANCELED;
- ret = 0;
- }
- if (rc == -ERESTARTSYS && freezing(current)) {
- clear_thread_flag(TIF_SIGPENDING);
- goto again;
+ add_wait_queue(queue, &wait);
+ for (;;) {
+ if (wait_for_tpm_stat_cond(chip, sts_mask, check_cancel,
+ &canceled)) {
+ ret = canceled ? -ECANCELED : 0;
+ break;
+ }
+
+ timeout = stop - jiffies;
+ if ((long)timeout <= 0)
+ break;
+
+ if (signal_pending(current)) {
+ if (freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ continue;
+ }
+ break;
+ }
+
+ wait_woken(&wait, TASK_INTERRUPTIBLE, timeout);
}
+ remove_wait_queue(queue, &wait);
}
if (ret)
--
2.47.3
^ permalink raw reply related
* [PATCH] dt-bindings: iio: adc: Convert xilinx-xadc bindings to YAML schema
From: Pramod Maurya @ 2026-05-09 17:14 UTC (permalink / raw)
To: jic23
Cc: pramod.nexgen, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michal Simek,
Lars-Peter Clausen, linux-iio, devicetree, linux-arm-kernel,
linux-kernel
Convert the Xilinx XADC and UltraScale System Monitor device tree binding
from the legacy plain-text format to a YAML schema, enabling automated
validation with dt-schema.
The new binding covers the same hardware and compatible strings:
- xlnx,zynq-xadc-1.00.a (ZYNQ hardmacro)
- xlnx,axi-xadc-1.00.a (AXI softmacro)
- xlnx,system-management-wiz-1.3 (UltraScale System Management Wizard)
Signed-off-by: Pramod Maurya <pramod.nexgen@gmail.com>
---
.../bindings/iio/adc/xilinx-xadc.txt | 141 -------------
.../bindings/iio/adc/xlnx,xadc.yaml | 191 ++++++++++++++++++
MAINTAINERS | 7 +
3 files changed, 198 insertions(+), 141 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
create mode 100644 Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt b/Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
deleted file mode 100644
index f42e18078376..000000000000
--- a/Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
+++ /dev/null
@@ -1,141 +0,0 @@
-Xilinx XADC device driver
-
-This binding document describes the bindings for the Xilinx 7 Series XADC as well
-as the UltraScale/UltraScale+ System Monitor.
-
-The Xilinx XADC is an ADC that can be found in the Series 7 FPGAs from Xilinx.
-The XADC has a DRP interface for communication. Currently two different
-frontends for the DRP interface exist. One that is only available on the ZYNQ
-family as a hardmacro in the SoC portion of the ZYNQ. The other one is available
-on all series 7 platforms and is a softmacro with a AXI interface. This binding
-document describes the bindings for both of them since the bindings are very
-similar.
-
-The Xilinx System Monitor is an ADC that is found in the UltraScale and
-UltraScale+ FPGAs from Xilinx. The System Monitor provides a DRP interface for
-communication. Xilinx provides a standard IP core that can be used to access the
-System Monitor through an AXI interface in the FPGA fabric. This IP core is
-called the Xilinx System Management Wizard. This document describes the bindings
-for this IP.
-
-Required properties:
- - compatible: Should be one of
- * "xlnx,zynq-xadc-1.00.a": When using the ZYNQ device
- configuration interface to interface to the XADC hardmacro.
- * "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
- interface to the XADC hardmacro.
- * "xlnx,system-management-wiz-1.3": When using the
- Xilinx System Management Wizard fabric IP core to access the
- UltraScale and UltraScale+ System Monitor.
- - reg: Address and length of the register set for the device
- - interrupts: Interrupt for the XADC control interface.
- - clocks: When using the ZYNQ this must be the ZYNQ PCAP clock,
- when using the axi-xadc or the axi-system-management-wizard this must be
- the clock that provides the clock to the AXI bus interface of the core.
-
-Optional properties:
- - xlnx,external-mux:
- * "none": No external multiplexer is used, this is the default
- if the property is omitted.
- * "single": External multiplexer mode is used with one
- multiplexer.
- * "dual": External multiplexer mode is used with two
- multiplexers for simultaneous sampling.
- - xlnx,external-mux-channel: Configures which pair of pins is used to
- sample data in external mux mode.
- Valid values for single external multiplexer mode are:
- 0: VP/VN
- 1: VAUXP[0]/VAUXN[0]
- 2: VAUXP[1]/VAUXN[1]
- ...
- 16: VAUXP[15]/VAUXN[15]
- Valid values for dual external multiplexer mode are:
- 1: VAUXP[0]/VAUXN[0] - VAUXP[8]/VAUXN[8]
- 2: VAUXP[1]/VAUXN[1] - VAUXP[9]/VAUXN[9]
- ...
- 8: VAUXP[7]/VAUXN[7] - VAUXP[15]/VAUXN[15]
-
- This property needs to be present if the device is configured for
- external multiplexer mode (either single or dual). If the device is
- not using external multiplexer mode the property is ignored.
- - xnlx,channels: List of external channels that are connected to the ADC
- Required properties:
- * #address-cells: Should be 1.
- * #size-cells: Should be 0.
-
- The child nodes of this node represent the external channels which are
- connected to the ADC. If the property is no present no external
- channels will be assumed to be connected.
-
- Each child node represents one channel and has the following
- properties:
- Required properties:
- * reg: Pair of pins the channel is connected to.
- 0: VP/VN
- 1: VAUXP[0]/VAUXN[0]
- 2: VAUXP[1]/VAUXN[1]
- ...
- 16: VAUXP[15]/VAUXN[15]
- Note each channel number should only be used at most
- once.
- Optional properties:
- * xlnx,bipolar: If set the channel is used in bipolar
- mode.
-
-
-Examples:
- xadc@f8007100 {
- compatible = "xlnx,zynq-xadc-1.00.a";
- reg = <0xf8007100 0x20>;
- interrupts = <0 7 4>;
- interrupt-parent = <&gic>;
- clocks = <&pcap_clk>;
-
- xlnx,channels {
- #address-cells = <1>;
- #size-cells = <0>;
- channel@0 {
- reg = <0>;
- };
- channel@1 {
- reg = <1>;
- };
- channel@8 {
- reg = <8>;
- };
- };
- };
-
- xadc@43200000 {
- compatible = "xlnx,axi-xadc-1.00.a";
- reg = <0x43200000 0x1000>;
- interrupts = <0 53 4>;
- interrupt-parent = <&gic>;
- clocks = <&fpga1_clk>;
-
- xlnx,channels {
- #address-cells = <1>;
- #size-cells = <0>;
- channel@0 {
- reg = <0>;
- xlnx,bipolar;
- };
- };
- };
-
- adc@80000000 {
- compatible = "xlnx,system-management-wiz-1.3";
- reg = <0x80000000 0x1000>;
- interrupts = <0 81 4>;
- interrupt-parent = <&gic>;
- clocks = <&fpga1_clk>;
-
- xlnx,channels {
- #address-cells = <1>;
- #size-cells = <0>;
- channel@0 {
- reg = <0>;
- xlnx,bipolar;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml b/Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
new file mode 100644
index 000000000000..86bdc4920cdb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
@@ -0,0 +1,191 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/xlnx,xadc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xilinx XADC and UltraScale System Monitor
+
+maintainers:
+ - Lars-Peter Clausen <lars@metafoo.de>
+
+description: |
+ The Xilinx XADC is an ADC found in the Series 7 FPGAs. It has a DRP
+ (Dynamic Reconfiguration Port) interface for communication. Two different
+ frontends for the DRP interface are supported:
+
+ - ZYNQ hardmacro: available only on the ZYNQ family as a hardmacro in
+ the SoC portion of the ZYNQ device.
+ - AXI softmacro: available on all Series 7 platforms as a softmacro
+ with an AXI interface (PG019).
+
+ The Xilinx System Monitor is an ADC found in UltraScale and UltraScale+
+ FPGAs. It is accessed through the Xilinx System Management Wizard IP core
+ via an AXI interface in the FPGA fabric.
+
+properties:
+ compatible:
+ enum:
+ - xlnx,zynq-xadc-1.00.a
+ - xlnx,axi-xadc-1.00.a
+ - xlnx,system-management-wiz-1.3
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ description: |
+ When using the ZYNQ this must be the ZYNQ PCAP clock.
+ When using the axi-xadc or system-management-wiz this must be
+ the clock that provides the clock to the AXI bus interface.
+ maxItems: 1
+
+ xlnx,external-mux:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: |
+ Selects the external multiplexer mode.
+ enum:
+ - none
+ - single
+ - dual
+
+ xlnx,external-mux-channel:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Configures which pair of pins is used to sample data in external
+ multiplexer mode. This property is required when the device is
+ configured for external multiplexer mode.
+
+ Valid values for single external multiplexer mode:
+ 0: VP/VN
+ 1-16: VAUXP[0]/VAUXN[0] through VAUXP[15]/VAUXN[15]
+
+ Valid values for dual external multiplexer mode:
+ 1: VAUXP[0]/VAUXN[0] - VAUXP[8]/VAUXN[8]
+ 2: VAUXP[1]/VAUXN[1] - VAUXP[9]/VAUXN[9]
+ ...
+ 8: VAUXP[7]/VAUXN[7] - VAUXP[15]/VAUXN[15]
+ minimum: 0
+ maximum: 16
+
+ xlnx,channels:
+ type: object
+ description:
+ List of external channels connected to the ADC. If this property is
+ absent, no external channels are assumed to be connected.
+
+ properties:
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ patternProperties:
+ "^channel@([0-9]|1[0-6])$":
+ type: object
+ description:
+ Represents an external channel connected to the ADC.
+
+ properties:
+ reg:
+ description: |
+ Pair of pins the channel is connected to.
+ 0: VP/VN
+ 1: VAUXP[0]/VAUXN[0]
+ 2: VAUXP[1]/VAUXN[1]
+ ...
+ 16: VAUXP[15]/VAUXN[15]
+ minimum: 0
+ maximum: 16
+
+ xlnx,bipolar:
+ $ref: /schemas/types.yaml#/definitions/flag
+ type: boolean
+ description:
+ If set, the channel is used in bipolar mode.
+
+ required:
+ - reg
+
+ additionalProperties: false
+
+ required:
+ - '#address-cells'
+ - '#size-cells'
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ /* ZYNQ hardmacro example */
+ adc@f8007100 {
+ compatible = "xlnx,zynq-xadc-1.00.a";
+ reg = <0xf8007100 0x20>;
+ interrupts = <0 7 4>;
+ interrupt-parent = <&gic>;
+ clocks = <&pcap_clk>;
+
+ xlnx,channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ };
+ channel@1 {
+ reg = <1>;
+ };
+ channel@8 {
+ reg = <8>;
+ };
+ };
+ };
+
+ - |
+ /* AXI softmacro example */
+ adc@43200000 {
+ compatible = "xlnx,axi-xadc-1.00.a";
+ reg = <0x43200000 0x1000>;
+ interrupts = <0 53 4>;
+ interrupt-parent = <&gic>;
+ clocks = <&fpga1_clk>;
+
+ xlnx,channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ xlnx,bipolar;
+ };
+ };
+ };
+
+ - |
+ /* UltraScale System Management Wizard example */
+ adc@80000000 {
+ compatible = "xlnx,system-management-wiz-1.3";
+ reg = <0x80000000 0x1000>;
+ interrupts = <0 81 4>;
+ interrupt-parent = <&gic>;
+ clocks = <&fpga1_clk>;
+
+ xlnx,channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ xlnx,bipolar;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..9b107057ad8c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29226,6 +29226,13 @@ F: Documentation/devicetree/bindings/watchdog/xlnx,xps-timebase-wdt.yaml
F: drivers/watchdog/of_xilinx_wdt.c
F: drivers/watchdog/xilinx_wwdt.c
+XILINX XADC DRIVER
+M: Lars-Peter Clausen <lars@metafoo.de>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/xlnx,xadc.yaml
+F: drivers/iio/adc/xilinx-xadc*
+
XILINX XDMA DRIVER
M: Lizhi Hou <lizhi.hou@amd.com>
M: Brian Xu <brian.xu@amd.com>
--
2.52.0
^ permalink raw reply related
* Re: [PATCH RFC] iommu: Enable per-device SSID space for SVA
From: Jason Gunthorpe @ 2026-05-09 17:10 UTC (permalink / raw)
To: Joonwon Kang
Cc: Alexander.Grest, amhetre, baolu.lu, iommu, joro, jpb, kees,
linux-arm-kernel, linux-kernel, nicolinc, praan, robin.murphy,
smostafa, will, jacob.jun.pan, easwar.hariharan, kevin.tian
In-Reply-To: <20260507095851.3220765-1-joonwonkang@google.com>
On Thu, May 07, 2026 at 09:58:51AM +0000, Joonwon Kang wrote:
> By "similar instruction" on ARM, I guess you mean ST64BV0, which fetches
> the bottom 32 bits data from ACCDATA_EL1. Please let me know if you meant
> others as it will matter. If ST64BV0 is supported on ARM, however, it
> would mean that ST64B and ST64BV are also supported already according to
> the ID_AA64ISAR1_EL1's LS64 field. The latter 2 instructions are just to
> atomically store whatever user wants to a memory location without
> referring to ACCDATA_EL1 and all the 3 instructions can be run at EL0. So,
> the userspace driver would have enough capability to designate arbitrary
> PASID as it wants via the latter 2 instructions when communicating with
> multiple devices.
IDK exactly what ARM did. IIRC on Intel ENQCMD forms a special
non-posted write TLP and the device can tell the TLP came from ENQCMD
and so it trusts the encoded PASID. ARM has to have done the same
thing - allowing anyone to forge the PASID by using a different
instruction misses the point of the Intel design.
Honestly, I'm not sure why they even implemented it. SMMUv3 can't do
the translation scheme required to use ENQCMD from a VM anyhow, so it
is pretty useless.
> We have multiple processes and a single device, those processes want to
> do SVA with the same device, and only one process will do SVA with the
> device at a time. Though, the problem occurs even when irrelevant
> processes allocate the PASIDs from the global PASID space for their own
> irrelevant purposes.
The only way to allocate a PASID from the global PASID space is to
establish another SVA, so you have multiple devices doing SVA?
Jason
^ permalink raw reply
* Re: [PATCH RFC] iommu: Enable per-device SSID space for SVA
From: Jason Gunthorpe @ 2026-05-09 17:03 UTC (permalink / raw)
To: Tian, Kevin
Cc: Joonwon Kang, will@kernel.org, robin.murphy@arm.com,
joro@8bytes.org, jpb@kernel.org, nicolinc@nvidia.com,
praan@google.com, kees@kernel.org, amhetre@nvidia.com,
Alexander.Grest@microsoft.com, baolu.lu@linux.intel.com,
smostafa@google.com, linux-arm-kernel@lists.infradead.org,
iommu@lists.linux.dev, linux-kernel@vger.kernel.org
In-Reply-To: <BN9PR11MB5276E9D968D3738CCE7125248C3C2@BN9PR11MB5276.namprd11.prod.outlook.com>
On Thu, May 07, 2026 at 08:15:21AM +0000, Tian, Kevin wrote:
> if both arch and device support enqcmd-like insn...
>
> I recalled this was discussed years ago. For devices like this, just
> let driver manage its own pasid space then have a new interface
> e.g. iommu_sva_bind_device_pasid(dev, mm, pasid) to use the
> specified pasid.
Yeah, that makes sense. If the driver knows it doesn't use an ENQCMD
like programming model at all then it can use this API and it should
also avoid programming the MSRs/etc.
Jason
^ permalink raw reply
* Re: [PATCH 7/9] iommu/arm-smmu-v3: Directly encode CMDQ_OP_ATC_INV
From: Jason Gunthorpe @ 2026-05-09 16:54 UTC (permalink / raw)
To: Pranjal Shrivastava
Cc: iommu, Jonathan Hunter, Joerg Roedel, linux-arm-kernel,
linux-tegra, Robin Murphy, Thierry Reding, Krishna Reddy,
Will Deacon, David Matlack, Pasha Tatashin, patches,
Samiullah Khawaja, Mostafa Saleh
In-Reply-To: <af3NH0cihPUQ8PST@google.com>
On Fri, May 08, 2026 at 11:46:39AM +0000, Pranjal Shrivastava wrote:
> On Fri, May 01, 2026 at 11:29:16AM -0300, Jason Gunthorpe wrote:
> > Add a new command make function and convert all the places using
> > ATC_INV.
> >
> > Split out full invalidation to directly make the cmd instead of
> > overloading size=0 to mean full invalidation.
> >
> > Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
> > ---
>
> Nit: I guess it's worth mentioning that we remove CMDQ_ATC_0_GLOBAL as
> we don't set ent->atc.global = true anywhere in the driver anymore.
I added this:
In section "3.9.1 ATS Interface" of F.b the specification says:
When the SMMU returns an ATS Translation Completion for a request that
had a PASID, the Global bit of the Translation Completion Data Entry
must be zero.
Even though it faithfully forwards the G bit through to the ATS
invalidation command there is no way to create G mappings so there is
never any need to send a G invalidation. Thus don't expose global in the
new helpers and leave CMDQ_ATC_0_GLOBAL unused.
Jason
^ permalink raw reply
* Re: [PATCH] mfd: twl4030-power: fix stale ARM machine ID checks to use the DT
From: Ethan Nelson-Moore @ 2026-05-09 16:44 UTC (permalink / raw)
To: linux-arm-kernel, linux-omap
Cc: Russell King, stable, Aaro Koskinen, Andreas Kemnade,
Kevin Hilman, Roger Quadros, Tony Lindgren, Lee Jones, Jon Hunter,
Benoit Cousson
In-Reply-To: <20260509014246.21649-1-enelsonmoore@gmail.com>
On Fri, May 8, 2026 at 6:43 PM Ethan Nelson-Moore
<enelsonmoore@gmail.com> wrote:
> The twl4030-power driver contains two checks for ARM machine IDs via
> machine_is_*() macros. These checks are incorrect because the two
> platforms concerned now support only FDT booting, which does not use
> machine IDs, and therefore they will always fail.
I have been informed that this is not correct and the kernel still
considers the machine ID when booting with FDT, though there is still
value in updating the check because it allows removing the machine IDs
from mach-types. I will resend this patch with a fixed commit message
after the 24-hour deadline.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox