mirror of
https://github.com/Ysurac/openmptcprouter.git
synced 2025-03-09 15:40:20 +00:00
Update kernel 6.12 patches
This commit is contained in:
parent
94a20cce9c
commit
4ca673613f
62 changed files with 7522 additions and 780 deletions
|
@ -19,8 +19,8 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
+
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/*
|
||||
@@ -314,6 +316,4 @@ static inline void *offset_to_ptr(const
|
||||
/**
|
||||
@@ -329,6 +331,4 @@ static inline void *offset_to_ptr(const
|
||||
*/
|
||||
#define prevent_tail_call_optimization() mb()
|
||||
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
From: Tobias Wolf <dev-NTEO@vplace.de>
|
||||
Subject: mm: Fix alloc_node_mem_map with ARCH_PFN_OFFSET calculation
|
||||
|
||||
An rt288x (ralink) based router (Belkin F5D8235 v1) does not boot with any
|
||||
kernel beyond version 4.3 resulting in:
|
||||
|
||||
BUG: Bad page state in process swapper pfn:086ac
|
||||
|
||||
bisect resulted in:
|
||||
|
||||
a1c34a3bf00af2cede839879502e12dc68491ad5 is the first bad commit
|
||||
commit a1c34a3bf00af2cede839879502e12dc68491ad5
|
||||
Author: Laura Abbott <laura@labbott.name>
|
||||
Date: Thu Nov 5 18:48:46 2015 -0800
|
||||
|
||||
mm: Don't offset memmap for flatmem
|
||||
|
||||
Srinivas Kandagatla reported bad page messages when trying to remove the
|
||||
bottom 2MB on an ARM based IFC6410 board
|
||||
|
||||
BUG: Bad page state in process swapper pfn:fffa8
|
||||
page:ef7fb500 count:0 mapcount:0 mapping: (null) index:0x0
|
||||
flags: 0x96640253(locked|error|dirty|active|arch_1|reclaim|mlocked)
|
||||
page dumped because: PAGE_FLAGS_CHECK_AT_FREE flag(s) set
|
||||
bad because of flags:
|
||||
flags: 0x200041(locked|active|mlocked)
|
||||
Modules linked in:
|
||||
CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00007-g412f9ba-dirty
|
||||
#816
|
||||
Hardware name: Qualcomm (Flattened Device Tree)
|
||||
unwind_backtrace
|
||||
show_stack
|
||||
dump_stack
|
||||
bad_page
|
||||
free_pages_prepare
|
||||
free_hot_cold_page
|
||||
__free_pages
|
||||
free_highmem_page
|
||||
mem_init
|
||||
start_kernel
|
||||
Disabling lock debugging due to kernel taint
|
||||
[...]
|
||||
:040000 040000 2de013c372345fd471cd58f0553c9b38b0ef1cc4
|
||||
0a8156f848733dfa21e16c196dfb6c0a76290709 M mm
|
||||
|
||||
This fix for ARM does not account ARCH_PFN_OFFSET for mem_map as later used by
|
||||
page_to_pfn anymore.
|
||||
|
||||
The following output was generated with two hacked in printk statements:
|
||||
|
||||
printk("before %p vs. %p or %p\n", mem_map, mem_map - offset, mem_map -
|
||||
(pgdat->node_start_pfn - ARCH_PFN_OFFSET));
|
||||
if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
|
||||
mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
|
||||
printk("after %p\n", mem_map);
|
||||
|
||||
Output:
|
||||
|
||||
[ 0.000000] before 8861b280 vs. 8861b280 or 8851b280
|
||||
[ 0.000000] after 8851b280
|
||||
|
||||
As seen in the first line mem_map with subtraction of offset does not equal the
|
||||
mem_map after subtraction of ARCH_PFN_OFFSET.
|
||||
|
||||
After adding the offset of ARCH_PFN_OFFSET as well to mem_map as the
|
||||
previously calculated offset is zero for the named platform it is able to boot
|
||||
4.4 and 4.9-rc7 again.
|
||||
|
||||
Signed-off-by: Tobias Wolf <dev-NTEO@vplace.de>
|
||||
---
|
||||
|
||||
--- a/mm/mm_init.c
|
||||
+++ b/mm/mm_init.c
|
||||
@@ -1632,7 +1632,7 @@ static void __init alloc_node_mem_map(st
|
||||
if (pgdat == NODE_DATA(0)) {
|
||||
mem_map = NODE_DATA(0)->node_mem_map;
|
||||
if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
|
||||
- mem_map -= offset;
|
||||
+ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Subject: kernel: add a config option for keeping the kallsyms table uncompressed, saving ~9kb kernel size after lzma on ar71xx
|
||||
|
||||
[john@phrozen.org: added to my upstream queue 30.12.2016]
|
||||
lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
init/Kconfig | 11 +++++++++++
|
||||
kernel/kallsyms.c | 8 ++++++++
|
||||
scripts/kallsyms.c | 12 ++++++++++++
|
||||
scripts/link-vmlinux.sh | 4 ++++
|
||||
4 files changed, 35 insertions(+)
|
||||
|
||||
--- a/init/Kconfig
|
||||
+++ b/init/Kconfig
|
||||
@@ -1525,6 +1525,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW
|
||||
the unaligned access emulation.
|
||||
see arch/parisc/kernel/unaligned.c for reference
|
||||
|
||||
+config KALLSYMS_UNCOMPRESSED
|
||||
+ bool "Keep kallsyms uncompressed"
|
||||
+ depends on KALLSYMS
|
||||
+ help
|
||||
+ Normally kallsyms contains compressed symbols (using a token table),
|
||||
+ reducing the uncompressed kernel image size. Keeping the symbol table
|
||||
+ uncompressed significantly improves the size of this part in compressed
|
||||
+ kernel images.
|
||||
+
|
||||
+ Say N unless you need compressed kernel images to be small.
|
||||
+
|
||||
config HAVE_PCSPKR_PLATFORM
|
||||
bool
|
||||
|
||||
--- a/kernel/kallsyms.c
|
||||
+++ b/kernel/kallsyms.c
|
||||
@@ -69,6 +69,11 @@ static unsigned int kallsyms_expand_symb
|
||||
* For every byte on the compressed symbol data, copy the table
|
||||
* entry for that byte.
|
||||
*/
|
||||
+#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
|
||||
+ memcpy(result, data + 1, len - 1);
|
||||
+ result += len - 1;
|
||||
+ len = 0;
|
||||
+#endif
|
||||
while (len) {
|
||||
tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
|
||||
data++;
|
||||
@@ -101,6 +106,9 @@ tail:
|
||||
*/
|
||||
static char kallsyms_get_symbol_type(unsigned int off)
|
||||
{
|
||||
+#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
|
||||
+ return kallsyms_names[off + 1];
|
||||
+#endif
|
||||
/*
|
||||
* Get just the first code, look it up in the token table,
|
||||
* and return the first char from this token.
|
||||
--- a/scripts/kallsyms.c
|
||||
+++ b/scripts/kallsyms.c
|
||||
@@ -62,6 +62,7 @@ static struct addr_range percpu_range =
|
||||
static struct sym_entry **table;
|
||||
static unsigned int table_size, table_cnt;
|
||||
static int all_symbols;
|
||||
+static int uncompressed;
|
||||
static int absolute_percpu;
|
||||
|
||||
static int token_profit[0x10000];
|
||||
@@ -412,12 +413,14 @@ static void write_src(void)
|
||||
for (k = 0; k < table[i]->len; k++)
|
||||
printf(", 0x%02x", table[i]->sym[k]);
|
||||
|
||||
- /*
|
||||
- * Now that we wrote out the compressed symbol name, restore the
|
||||
- * original name and print it in the comment.
|
||||
- */
|
||||
- expand_symbol(table[i]->sym, table[i]->len, buf);
|
||||
- strcpy((char *)table[i]->sym, buf);
|
||||
+ if (!uncompressed) {
|
||||
+ /*
|
||||
+ * Now that we wrote out the compressed symbol name, restore the
|
||||
+ * original name and print it in the comment.
|
||||
+ */
|
||||
+ expand_symbol(table[i]->sym, table[i]->len, buf);
|
||||
+ strcpy((char *)table[i]->sym, buf);
|
||||
+ }
|
||||
printf("\t/* %s */\n", table[i]->sym);
|
||||
}
|
||||
printf("\n");
|
||||
@@ -429,20 +432,22 @@ static void write_src(void)
|
||||
|
||||
free(markers);
|
||||
|
||||
- output_label("kallsyms_token_table");
|
||||
- off = 0;
|
||||
- for (i = 0; i < 256; i++) {
|
||||
- best_idx[i] = off;
|
||||
- expand_symbol(best_table[i], best_table_len[i], buf);
|
||||
- printf("\t.asciz\t\"%s\"\n", buf);
|
||||
- off += strlen(buf) + 1;
|
||||
+ if (!uncompressed) {
|
||||
+ output_label("kallsyms_token_table");
|
||||
+ off = 0;
|
||||
+ for (i = 0; i < 256; i++) {
|
||||
+ best_idx[i] = off;
|
||||
+ expand_symbol(best_table[i], best_table_len[i], buf);
|
||||
+ printf("\t.asciz\t\"%s\"\n", buf);
|
||||
+ off += strlen(buf) + 1;
|
||||
+ }
|
||||
+ printf("\n");
|
||||
+
|
||||
+ output_label("kallsyms_token_index");
|
||||
+ for (i = 0; i < 256; i++)
|
||||
+ printf("\t.short\t%d\n", best_idx[i]);
|
||||
+ printf("\n");
|
||||
}
|
||||
- printf("\n");
|
||||
-
|
||||
- output_label("kallsyms_token_index");
|
||||
- for (i = 0; i < 256; i++)
|
||||
- printf("\t.short\t%d\n", best_idx[i]);
|
||||
- printf("\n");
|
||||
|
||||
output_label("kallsyms_offsets");
|
||||
|
||||
@@ -532,6 +537,9 @@ static unsigned char *find_token(unsigne
|
||||
{
|
||||
int i;
|
||||
|
||||
+ if (uncompressed)
|
||||
+ return NULL;
|
||||
+
|
||||
for (i = 0; i < len - 1; i++) {
|
||||
if (str[i] == token[0] && str[i+1] == token[1])
|
||||
return &str[i];
|
||||
@@ -604,6 +612,9 @@ static void optimize_result(void)
|
||||
{
|
||||
int i, best;
|
||||
|
||||
+ if (uncompressed)
|
||||
+ return;
|
||||
+
|
||||
/* using the '\0' symbol last allows compress_symbols to use standard
|
||||
* fast string functions */
|
||||
for (i = 255; i >= 0; i--) {
|
||||
@@ -763,6 +774,7 @@ int main(int argc, char **argv)
|
||||
static const struct option long_options[] = {
|
||||
{"all-symbols", no_argument, &all_symbols, 1},
|
||||
{"absolute-percpu", no_argument, &absolute_percpu, 1},
|
||||
+ {"uncompressed", no_argument, &uncompressed, 1},
|
||||
{},
|
||||
};
|
||||
|
||||
--- a/scripts/link-vmlinux.sh
|
||||
+++ b/scripts/link-vmlinux.sh
|
||||
@@ -144,6 +144,10 @@ kallsyms()
|
||||
kallsymopt="${kallsymopt} --absolute-percpu"
|
||||
fi
|
||||
|
||||
+ if is_enabled CONFIG_KALLSYMS_UNCOMPRESSED; then
|
||||
+ kallsymopt="${kallsymopt} --uncompressed"
|
||||
+ fi
|
||||
+
|
||||
info KSYMS "${2}.S"
|
||||
scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S"
|
||||
|
|
@ -156,7 +156,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
|||
+device_initcall(blk_notifications_init);
|
||||
--- a/include/linux/blkdev.h
|
||||
+++ b/include/linux/blkdev.h
|
||||
@@ -1689,4 +1689,15 @@ static inline bool bdev_can_atomic_write
|
||||
@@ -1690,4 +1690,15 @@ static inline bool bdev_can_atomic_write
|
||||
|
||||
#define DEFINE_IO_COMP_BATCH(name) struct io_comp_batch name = { }
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
#define PACKET_FANOUT_LB 1
|
||||
--- a/net/packet/af_packet.c
|
||||
+++ b/net/packet/af_packet.c
|
||||
@@ -1925,6 +1925,7 @@ static int packet_rcv_spkt(struct sk_buf
|
||||
@@ -1911,6 +1911,7 @@ static int packet_rcv_spkt(struct sk_buf
|
||||
{
|
||||
struct sock *sk;
|
||||
struct sockaddr_pkt *spkt;
|
||||
|
@ -38,7 +38,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
|
||||
/*
|
||||
* When we registered the protocol we saved the socket in the data
|
||||
@@ -1932,6 +1933,7 @@ static int packet_rcv_spkt(struct sk_buf
|
||||
@@ -1918,6 +1919,7 @@ static int packet_rcv_spkt(struct sk_buf
|
||||
*/
|
||||
|
||||
sk = pt->af_packet_priv;
|
||||
|
@ -46,7 +46,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
|
||||
/*
|
||||
* Yank back the headers [hope the device set this
|
||||
@@ -1944,7 +1946,7 @@ static int packet_rcv_spkt(struct sk_buf
|
||||
@@ -1930,7 +1932,7 @@ static int packet_rcv_spkt(struct sk_buf
|
||||
* so that this procedure is noop.
|
||||
*/
|
||||
|
||||
|
@ -55,7 +55,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
goto out;
|
||||
|
||||
if (!net_eq(dev_net(dev), sock_net(sk)))
|
||||
@@ -2189,12 +2191,12 @@ static int packet_rcv(struct sk_buff *sk
|
||||
@@ -2175,12 +2177,12 @@ static int packet_rcv(struct sk_buff *sk
|
||||
int skb_len = skb->len;
|
||||
unsigned int snaplen, res;
|
||||
|
||||
|
@ -71,7 +71,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
if (!net_eq(dev_net(dev), sock_net(sk)))
|
||||
goto drop;
|
||||
|
||||
@@ -2318,12 +2320,12 @@ static int tpacket_rcv(struct sk_buff *s
|
||||
@@ -2304,12 +2306,12 @@ static int tpacket_rcv(struct sk_buff *s
|
||||
BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32);
|
||||
BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48);
|
||||
|
||||
|
@ -87,7 +87,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
if (!net_eq(dev_net(dev), sock_net(sk)))
|
||||
goto drop;
|
||||
|
||||
@@ -3444,6 +3446,7 @@ static int packet_create(struct net *net
|
||||
@@ -3430,6 +3432,7 @@ static int packet_create(struct net *net
|
||||
mutex_init(&po->pg_vec_lock);
|
||||
po->rollover = NULL;
|
||||
po->prot_hook.func = packet_rcv;
|
||||
|
@ -95,7 +95,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
|
||||
if (sock->type == SOCK_PACKET)
|
||||
po->prot_hook.func = packet_rcv_spkt;
|
||||
@@ -4111,6 +4114,16 @@ packet_setsockopt(struct socket *sock, i
|
||||
@@ -4097,6 +4100,16 @@ packet_setsockopt(struct socket *sock, i
|
||||
packet_sock_flag_set(po, PACKET_SOCK_QDISC_BYPASS, val);
|
||||
return 0;
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
@@ -4173,6 +4186,13 @@ static int packet_getsockopt(struct sock
|
||||
@@ -4159,6 +4172,13 @@ static int packet_getsockopt(struct sock
|
||||
case PACKET_COPY_THRESH:
|
||||
val = READ_ONCE(pkt_sk(sk)->copy_thresh);
|
||||
break;
|
||||
|
|
|
@ -96,7 +96,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
}
|
||||
--- a/net/core/sock.c
|
||||
+++ b/net/core/sock.c
|
||||
@@ -2525,7 +2525,7 @@ void sk_setup_caps(struct sock *sk, stru
|
||||
@@ -2528,7 +2528,7 @@ void sk_setup_caps(struct sock *sk, stru
|
||||
if (sk_is_tcp(sk))
|
||||
sk->sk_route_caps |= NETIF_F_GSO;
|
||||
if (sk->sk_route_caps & NETIF_F_GSO)
|
||||
|
@ -107,7 +107,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
if (sk_can_gso(sk)) {
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -2012,7 +2012,7 @@ void ieee80211_color_collision_detection
|
||||
@@ -2010,7 +2010,7 @@ void ieee80211_color_collision_detection
|
||||
/* interface handling */
|
||||
#define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
|
||||
NETIF_F_HW_CSUM | NETIF_F_SG | \
|
||||
|
|
|
@ -18,7 +18,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -8607,7 +8607,7 @@ static int nft_register_flowtable_net_ho
|
||||
@@ -8615,7 +8615,7 @@ static int nft_register_flowtable_net_ho
|
||||
err = flowtable->data.type->setup(&flowtable->data,
|
||||
hook->ops.dev,
|
||||
FLOW_BLOCK_BIND);
|
||||
|
|
|
@ -10,12 +10,12 @@ the PHY.
|
|||
Reported-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/realtek.c | 6 ++++++
|
||||
drivers/net/phy/realtek/realtek_main.c | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/realtek.c
|
||||
+++ b/drivers/net/phy/realtek.c
|
||||
@@ -1375,6 +1375,7 @@ static struct phy_driver realtek_drvs[]
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -1430,6 +1430,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.name = "RTL8226 2.5Gbps PHY",
|
||||
.match_phy_device = rtl8226_match_phy_device,
|
||||
|
@ -23,15 +23,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
|||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
.read_status = rtl822x_read_status,
|
||||
@@ -1387,6 +1388,7 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1440,6 +1441,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
PHY_ID_MATCH_EXACT(0x001cc840),
|
||||
.match_phy_device = rtl8221b_match_phy_device,
|
||||
.name = "RTL8226B_RTL8221B 2.5Gbps PHY",
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
.config_init = rtl822xb_config_init,
|
||||
@@ -1401,6 +1403,7 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1452,6 +1454,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
PHY_ID_MATCH_EXACT(0x001cc838),
|
||||
.name = "RTL8226-CG 2.5Gbps PHY",
|
||||
|
@ -39,7 +39,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
|||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
.read_status = rtl822x_read_status,
|
||||
@@ -1411,6 +1414,7 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1462,6 +1465,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
PHY_ID_MATCH_EXACT(0x001cc848),
|
||||
.name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
|
||||
|
@ -47,35 +47,35 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
|||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
.config_init = rtl822xb_config_init,
|
||||
@@ -1423,6 +1427,7 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1474,6 +1478,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
|
||||
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
.config_init = rtl822xb_config_init,
|
||||
@@ -1435,6 +1440,7 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1487,6 +1492,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
|
||||
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.config_init = rtl822xb_config_init,
|
||||
.get_rate_matching = rtl822xb_get_rate_matching,
|
||||
.get_features = rtl822x_c45_get_features,
|
||||
@@ -1445,6 +1451,7 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1498,6 +1504,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
|
||||
.name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
.config_init = rtl822xb_config_init,
|
||||
@@ -1457,6 +1464,7 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1511,6 +1518,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
|
||||
.name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.config_init = rtl822xb_config_init,
|
||||
.get_rate_matching = rtl822xb_get_rate_matching,
|
||||
.get_features = rtl822x_c45_get_features,
|
||||
|
|
|
@ -15,12 +15,12 @@ Reported-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
|
|||
Tested-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/realtek.c | 27 +++++++++++++++++++++++++--
|
||||
drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++++++++++--
|
||||
1 file changed, 25 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/realtek.c
|
||||
+++ b/drivers/net/phy/realtek.c
|
||||
@@ -814,8 +814,8 @@ static int rtl822x_write_mmd(struct phy_
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -834,8 +834,8 @@ static int rtl822x_probe(struct phy_devi
|
||||
static int rtl822xb_config_init(struct phy_device *phydev)
|
||||
{
|
||||
bool has_2500, has_sgmii;
|
||||
|
@ -30,7 +30,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
|||
|
||||
has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX,
|
||||
phydev->host_interfaces) ||
|
||||
@@ -865,7 +865,29 @@ static int rtl822xb_config_init(struct p
|
||||
@@ -885,7 +885,29 @@ static int rtl822xb_config_init(struct p
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ rtl821x_write_page instead of 3 individually locked MDIO bus operations.
|
|||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/realtek.c | 8 +++++---
|
||||
drivers/net/phy/realtek/realtek_main.c | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/realtek.c
|
||||
+++ b/drivers/net/phy/realtek.c
|
||||
@@ -1092,9 +1092,11 @@ static bool rtlgen_supports_2_5gbps(stru
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -1111,9 +1111,11 @@ static bool rtlgen_supports_2_5gbps(stru
|
||||
{
|
||||
int val;
|
||||
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
From 9155098547fb1172d4fa536f3f6bc9d42f59d08c Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Sat, 22 Apr 2023 03:26:01 +0100
|
||||
Subject: [PATCH] net: phy: realtek: setup ALDPS on RTL822x
|
||||
|
||||
Setup Link Down Power Saving Mode according the DTS property
|
||||
just like for RTL821x 1GE PHYs.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/realtek.c | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/realtek.c
|
||||
+++ b/drivers/net/phy/realtek.c
|
||||
@@ -80,6 +80,10 @@
|
||||
|
||||
#define RTL822X_VND2_GANLPAR 0xa414
|
||||
|
||||
+#define RTL8221B_PHYCR1 0xa430
|
||||
+#define RTL8221B_PHYCR1_ALDPS_EN BIT(2)
|
||||
+#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12)
|
||||
+
|
||||
#define RTL8366RB_POWER_SAVE 0x15
|
||||
#define RTL8366RB_POWER_SAVE_ON BIT(12)
|
||||
|
||||
@@ -1152,6 +1156,25 @@ static int rtl8251b_c45_match_phy_device
|
||||
return rtlgen_is_c45_match(phydev, RTL_8251B, true);
|
||||
}
|
||||
|
||||
+static int rtl822x_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ int val;
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL8221B_PHYCR1);
|
||||
+ if (val < 0)
|
||||
+ return val;
|
||||
+
|
||||
+ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
|
||||
+ val |= RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN;
|
||||
+ else
|
||||
+ val &= ~(RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN);
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL8221B_PHYCR1, val);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int rtlgen_resume(struct phy_device *phydev)
|
||||
{
|
||||
int ret = genphy_resume(phydev);
|
||||
@@ -1427,6 +1450,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
PHY_ID_MATCH_EXACT(0x001cc838),
|
||||
.name = "RTL8226-CG 2.5Gbps PHY",
|
||||
+ .probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
@@ -1438,6 +1462,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
PHY_ID_MATCH_EXACT(0x001cc848),
|
||||
.name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
|
||||
+ .probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
@@ -1451,6 +1476,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
|
||||
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
|
||||
+ .probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
@@ -1464,6 +1490,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
|
||||
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
|
||||
+ .probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_init = rtl822xb_config_init,
|
||||
.get_rate_matching = rtl822xb_get_rate_matching,
|
||||
@@ -1475,6 +1502,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
|
||||
.name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
|
||||
+ .probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_features = rtl822x_get_features,
|
||||
.config_aneg = rtl822x_config_aneg,
|
||||
@@ -1488,6 +1516,7 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
|
||||
.name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
|
||||
+ .probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_init = rtl822xb_config_init,
|
||||
.get_rate_matching = rtl822xb_get_rate_matching,
|
|
@ -0,0 +1,42 @@
|
|||
From 9155098547fb1172d4fa536f3f6bc9d42f59d08c Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Sat, 22 Apr 2023 03:26:01 +0100
|
||||
Subject: [PATCH] net: phy: realtek: setup ALDPS on RTL822x
|
||||
|
||||
Setup Link Down Power Saving Mode according the DTS property
|
||||
just like for RTL821x 1GE PHYs.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/realtek/realtek_main.c | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -82,6 +82,10 @@
|
||||
|
||||
#define RTL822X_VND2_GANLPAR 0xa414
|
||||
|
||||
+#define RTL8221B_PHYCR1 0xa430
|
||||
+#define RTL8221B_PHYCR1_ALDPS_EN BIT(2)
|
||||
+#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12)
|
||||
+
|
||||
#define RTL8366RB_POWER_SAVE 0x15
|
||||
#define RTL8366RB_POWER_SAVE_ON BIT(12)
|
||||
|
||||
@@ -889,6 +893,15 @@ static int rtl822xb_config_init(struct p
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
+ if (of_property_read_bool(phydev->mdio.dev.of_node, "realtek,aldps-enable"))
|
||||
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, RTL8221B_PHYCR1,
|
||||
+ RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN);
|
||||
+ else
|
||||
+ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, RTL8221B_PHYCR1,
|
||||
+ RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
/* Disable SGMII AN */
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7588, 0x2);
|
||||
if (ret < 0)
|
|
@ -12,9 +12,9 @@ over the implemented MMDs.
|
|||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
[forward-port by @namiltd]
|
||||
Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
|
||||
--- a/drivers/net/phy/realtek.c
|
||||
+++ b/drivers/net/phy/realtek.c
|
||||
@@ -1120,10 +1120,32 @@ static int rtl8226_match_phy_device(stru
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -1166,10 +1166,32 @@ static int rtl8226_match_phy_device(stru
|
||||
static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
|
||||
bool is_c45)
|
||||
{
|
||||
|
@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
|
|||
+ }
|
||||
}
|
||||
|
||||
static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
|
||||
static int rtl8221b_match_phy_device(struct phy_device *phydev)
|
||||
|
|
|
@ -7,12 +7,12 @@ This commit introduces interrupt support for RTL8221B.
|
|||
|
||||
Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
|
||||
---
|
||||
drivers/net/phy/realtek.c | 47 +++++++++++++++++++++++++++++++++++++++
|
||||
drivers/net/phy/realtek/realtek_main.c | 47 +++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 47 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/realtek.c
|
||||
+++ b/drivers/net/phy/realtek.c
|
||||
@@ -1332,6 +1332,51 @@ static irqreturn_t rtl9000a_handle_inter
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -1377,6 +1377,51 @@ static irqreturn_t rtl9000a_handle_inter
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -64,39 +64,39 @@ Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
|
|||
static struct phy_driver realtek_drvs[] = {
|
||||
{
|
||||
PHY_ID_MATCH_EXACT(0x00008201),
|
||||
@@ -1498,6 +1543,8 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1537,6 +1582,8 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
|
||||
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
|
||||
+ .config_intr = rtl8221b_config_intr,
|
||||
+ .handle_interrupt = rtl8221b_handle_interrupt,
|
||||
.probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.get_features = rtl822x_get_features,
|
||||
@@ -1512,6 +1559,8 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1551,6 +1598,8 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
|
||||
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
|
||||
+ .config_intr = rtl8221b_config_intr,
|
||||
+ .handle_interrupt = rtl8221b_handle_interrupt,
|
||||
.probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.config_init = rtl822xb_config_init,
|
||||
@@ -1524,6 +1573,8 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1563,6 +1612,8 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
|
||||
.name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
|
||||
+ .config_intr = rtl8221b_config_intr,
|
||||
+ .handle_interrupt = rtl8221b_handle_interrupt,
|
||||
.probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.get_features = rtl822x_get_features,
|
||||
@@ -1538,6 +1589,8 @@ static struct phy_driver realtek_drvs[]
|
||||
@@ -1577,6 +1628,8 @@ static struct phy_driver realtek_drvs[]
|
||||
}, {
|
||||
.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
|
||||
.name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
|
||||
+ .config_intr = rtl8221b_config_intr,
|
||||
+ .handle_interrupt = rtl8221b_handle_interrupt,
|
||||
.probe = rtl822x_probe,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
.config_init = rtl822xb_config_init,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
From 1addfb042a9d27788a0fb2c2935045b56fd8560e Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 23 Jan 2025 03:25:29 +0000
|
||||
Subject: [PATCH] net: phy: realtek: mark existing MMDs as present
|
||||
|
||||
When using Clause-45 mode to access RealTek RTL8221B 2.5G PHYs some
|
||||
versions of the PHY fail to report the MMDs present on the PHY.
|
||||
Mark MMDs PMAPMD, PCS and AN which are always existing according to
|
||||
the datasheet as present to fix that.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/realtek/realtek_main.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -1043,6 +1043,9 @@ static int rtl822x_c45_get_features(stru
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT,
|
||||
phydev->supported);
|
||||
|
||||
+ phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
|
||||
+ MDIO_DEVS_AN;
|
||||
+
|
||||
return genphy_c45_pma_read_abilities(phydev);
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 30 Jan 2025 05:33:12 +0000
|
||||
Subject: [PATCH] net: phy: realtek: work around broken SerDes
|
||||
|
||||
For still unknown reasons the SerDes init sequence may sometimes
|
||||
time out because a self-clearing bit never clears, indicating the
|
||||
PHY has entered an unrecoverable error state.
|
||||
|
||||
Work-around the issue by triggering a hardware reset and retry the
|
||||
setup sequence while warning the user that this has happened.
|
||||
This is really more of a work-around than a fix, and should be
|
||||
replaced by a better actual fix in future (hopefully).
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -923,6 +923,22 @@ static int rtl822xb_config_init(struct p
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int rtl822xb_config_init_war(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = rtl822xb_config_init(phydev);
|
||||
+
|
||||
+ if (ret == -ETIMEDOUT) {
|
||||
+ phydev_warn(phydev, "SerDes setup timed out, retrying\n");
|
||||
+ phy_device_reset(phydev, 1);
|
||||
+ phy_device_reset(phydev, 0);
|
||||
+ ret = rtl822xb_config_init(phydev);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int rtl822xb_get_rate_matching(struct phy_device *phydev,
|
||||
phy_interface_t iface)
|
||||
{
|
||||
@@ -1605,7 +1621,7 @@ static struct phy_driver realtek_drvs[]
|
||||
.handle_interrupt = rtl8221b_handle_interrupt,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
- .config_init = rtl822xb_config_init,
|
||||
+ .config_init = rtl822xb_config_init_war,
|
||||
.get_rate_matching = rtl822xb_get_rate_matching,
|
||||
.get_features = rtl822x_c45_get_features,
|
||||
.config_aneg = rtl822x_c45_config_aneg,
|
||||
@@ -1635,7 +1651,7 @@ static struct phy_driver realtek_drvs[]
|
||||
.handle_interrupt = rtl8221b_handle_interrupt,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.probe = rtl822x_probe,
|
||||
- .config_init = rtl822xb_config_init,
|
||||
+ .config_init = rtl822xb_config_init_war,
|
||||
.get_rate_matching = rtl822xb_get_rate_matching,
|
||||
.get_features = rtl822x_c45_get_features,
|
||||
.config_aneg = rtl822x_c45_config_aneg,
|
|
@ -0,0 +1,27 @@
|
|||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 30 Jan 2025 05:38:31 +0000
|
||||
Subject: [PATCH] net: phy: realtek: disable MDIO broadcast
|
||||
|
||||
RealTek's PHYs by default also listen on MDIO address 0 which is defined
|
||||
as broadcast address. This can lead to problems if there is an actual PHY
|
||||
(such as MT7981 built-in PHY) present at this address, as accessing that
|
||||
PHY may then confuse the RealTek PHY.
|
||||
|
||||
Disabled listening on the MDIO broadcast address to avoid such problems.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
--- a/drivers/net/phy/realtek/realtek_main.c
|
||||
+++ b/drivers/net/phy/realtek/realtek_main.c
|
||||
@@ -849,6 +849,11 @@ static int rtl822xb_config_init(struct p
|
||||
phydev->host_interfaces) ||
|
||||
phydev->interface == PHY_INTERFACE_MODE_SGMII;
|
||||
|
||||
+ /* disable listening on MDIO broadcast address (0) */
|
||||
+ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, 0xa430, BIT(13));
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
/* fill in possible interfaces */
|
||||
__assign_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces,
|
||||
has_2500);
|
|
@ -0,0 +1,104 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Subject: [PATCH net-next 3/4] net: ethernet: mtk_eth_soc: reduce rx ring size for older chipsets
|
||||
Date: Tue, 15 Oct 2024 13:09:37 +0200
|
||||
|
||||
Commit c57e55819443 ("net: ethernet: mtk_eth_soc: handle dma buffer
|
||||
size soc specific") resolved some tx timeout issues by bumping FQ and
|
||||
tx ring sizes from 512 to 2048 entries (the value used in the MediaTek
|
||||
SDK), however it also changed the rx ring size for all chipsets in the
|
||||
same way.
|
||||
|
||||
Based on a few tests, it seems that a symmetric rx/tx ring size of 2048
|
||||
really only makes sense on MT7988, which is capable of 10G ethernet links.
|
||||
|
||||
Older chipsets are typically deployed in systems that are more memory
|
||||
constrained and don't actually need the larger rings to handle received
|
||||
packets.
|
||||
|
||||
In order to reduce wasted memory set the ring size based on the SoC to
|
||||
the following values:
|
||||
- 2048 on MT7988
|
||||
- 1024 on MT7986
|
||||
- 512 (previous value) on everything else, except:
|
||||
- 256 on RT5350 (the oldest supported chipset)
|
||||
|
||||
Fixes: c57e55819443 ("net: ethernet: mtk_eth_soc: handle dma buffer size soc specific")
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 ++++++++--------
|
||||
1 file changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -5387,7 +5387,7 @@ static const struct mtk_soc_data mt2701_
|
||||
.desc_size = sizeof(struct mtk_rx_dma),
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(512),
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
},
|
||||
@@ -5415,7 +5415,7 @@ static const struct mtk_soc_data mt7621_
|
||||
.desc_size = sizeof(struct mtk_rx_dma),
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(512),
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
},
|
||||
@@ -5445,7 +5445,7 @@ static const struct mtk_soc_data mt7622_
|
||||
.desc_size = sizeof(struct mtk_rx_dma),
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(512),
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
},
|
||||
@@ -5474,7 +5474,7 @@ static const struct mtk_soc_data mt7623_
|
||||
.desc_size = sizeof(struct mtk_rx_dma),
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(512),
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
},
|
||||
@@ -5500,7 +5500,7 @@ static const struct mtk_soc_data mt7629_
|
||||
.desc_size = sizeof(struct mtk_rx_dma),
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(512),
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
},
|
||||
@@ -5532,7 +5532,7 @@ static const struct mtk_soc_data mt7981_
|
||||
.dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(512),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5562,7 +5562,7 @@ static const struct mtk_soc_data mt7986_
|
||||
.dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(1K),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5615,7 +5615,7 @@ static const struct mtk_soc_data rt5350_
|
||||
.dma_l4_valid = RX_DMA_L4_VALID_PDMA,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = 16,
|
||||
- .dma_size = MTK_DMA_SIZE(2K),
|
||||
+ .dma_size = MTK_DMA_SIZE(256),
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
From: Danila Romanov <pervokur@gmail.com>
|
||||
Date: Wed, 22 Jan 2025 06:48:45 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: do not enable page pool stats by
|
||||
default
|
||||
|
||||
There is no reason for it to be enabled by default.
|
||||
Align mtk_eth_soc driver to mt76 driver.
|
||||
|
||||
This option incurs additional CPU cost in allocation and recycle paths
|
||||
and additional memory cost to store the statistics.
|
||||
|
||||
Signed-off-by: Danila Romanov <pervokur@gmail.com>
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/Kconfig
|
||||
+++ b/drivers/net/ethernet/mediatek/Kconfig
|
||||
@@ -26,7 +26,6 @@ config NET_MEDIATEK_SOC
|
||||
select PHYLINK
|
||||
select DIMLIB
|
||||
select PAGE_POOL
|
||||
- select PAGE_POOL_STATS
|
||||
select PCS_MTK_LYNXI
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -4552,6 +4552,7 @@ static int mtk_get_sset_count(struct net
|
||||
|
||||
static void mtk_ethtool_pp_stats(struct mtk_eth *eth, u64 *data)
|
||||
{
|
||||
+#ifdef CONFIG_PAGE_POOL_STATS
|
||||
struct page_pool_stats stats = {};
|
||||
int i;
|
||||
|
||||
@@ -4564,6 +4565,7 @@ static void mtk_ethtool_pp_stats(struct
|
||||
page_pool_get_stats(ring->page_pool, &stats);
|
||||
}
|
||||
page_pool_ethtool_stats_get(data, &stats);
|
||||
+#endif
|
||||
}
|
||||
|
||||
static void mtk_get_ethtool_stats(struct net_device *dev,
|
|
@ -0,0 +1,248 @@
|
|||
From fd0e523037439520813db7c57df5bd37cdf40f7e Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 3 Feb 2025 00:10:18 +0100
|
||||
Subject: [PATCH 1/2] nvmem: core: generalize "mac-base" cells handling
|
||||
|
||||
Generalize support of "mac-base" nvmem cells and provide a GPL symbol to
|
||||
permit also other NVMEM layout driver to parse mac-base cells.
|
||||
|
||||
It's VERY COMMON for some specially formatted NVMEM to expose a mac
|
||||
address in ASCII format or HEX format hence prevent code duplication by
|
||||
exposing a common helper.
|
||||
|
||||
Such helper will change the nvmem_info_cell and apply the correct post
|
||||
process function to correctly parse the mac address.
|
||||
|
||||
Since the API requires OF and is only related to layout, move the
|
||||
function in layouts.c to correctly handle non-OF NVMEM.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/nvmem/core.c | 79 +--------------------------------
|
||||
drivers/nvmem/layouts.c | 80 ++++++++++++++++++++++++++++++++++
|
||||
include/linux/nvmem-provider.h | 4 ++
|
||||
3 files changed, 86 insertions(+), 77 deletions(-)
|
||||
|
||||
--- a/drivers/nvmem/core.c
|
||||
+++ b/drivers/nvmem/core.c
|
||||
@@ -7,12 +7,9 @@
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
-#include <linux/ctype.h>
|
||||
-#include <linux/etherdevice.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/idr.h>
|
||||
-#include <linux/if_ether.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/module.h>
|
||||
@@ -800,62 +797,6 @@ static int nvmem_validate_keepouts(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset,
|
||||
- void *buf, size_t bytes)
|
||||
-{
|
||||
- if (WARN_ON(bytes != ETH_ALEN))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- if (index)
|
||||
- eth_addr_add(buf, index);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset,
|
||||
- void *buf, size_t bytes)
|
||||
-{
|
||||
- u8 mac[ETH_ALEN];
|
||||
-
|
||||
- if (WARN_ON(bytes != 3 * ETH_ALEN - 1))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- if (!mac_pton(buf, mac))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- if (index)
|
||||
- eth_addr_add(mac, index);
|
||||
-
|
||||
- ether_addr_copy(buf, mac);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset,
|
||||
- void *buf, size_t bytes)
|
||||
-{
|
||||
- u8 mac[ETH_ALEN], *hexstr;
|
||||
- int i;
|
||||
-
|
||||
- if (WARN_ON(bytes != 2 * ETH_ALEN))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- hexstr = (u8 *)buf;
|
||||
- for (i = 0; i < ETH_ALEN; i++) {
|
||||
- if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1]))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]);
|
||||
- }
|
||||
-
|
||||
- if (index)
|
||||
- eth_addr_add(mac, index);
|
||||
-
|
||||
- ether_addr_copy(buf, mac);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
|
||||
{
|
||||
struct device *dev = &nvmem->dev;
|
||||
@@ -895,24 +836,8 @@ static int nvmem_add_cells_from_dt(struc
|
||||
if (nvmem->fixup_dt_cell_info)
|
||||
nvmem->fixup_dt_cell_info(nvmem, &info);
|
||||
|
||||
- if (of_device_is_compatible(np, "fixed-layout")) {
|
||||
- if (of_device_is_compatible(child, "mac-base")) {
|
||||
- if (info.bytes == ETH_ALEN) {
|
||||
- info.raw_len = info.bytes;
|
||||
- info.bytes = ETH_ALEN;
|
||||
- info.read_post_process = nvmem_mac_base_raw_read;
|
||||
- } else if (info.bytes == 2 * ETH_ALEN) {
|
||||
- info.raw_len = info.bytes;
|
||||
- info.bytes = ETH_ALEN;
|
||||
- info.read_post_process = nvmem_mac_base_hex_read;
|
||||
- } else if (info.bytes == 3 * ETH_ALEN - 1) {
|
||||
- info.raw_len = info.bytes;
|
||||
- info.bytes = ETH_ALEN;
|
||||
- info.read_post_process = nvmem_mac_base_ascii_read;
|
||||
- }
|
||||
-
|
||||
- }
|
||||
- }
|
||||
+ if (of_device_is_compatible(np, "fixed-layout"))
|
||||
+ nvmem_layout_parse_mac_base(&info);
|
||||
|
||||
ret = nvmem_add_one_cell(nvmem, &info);
|
||||
kfree(info.name);
|
||||
--- a/drivers/nvmem/layouts.c
|
||||
+++ b/drivers/nvmem/layouts.c
|
||||
@@ -6,8 +6,11 @@
|
||||
* Author: Miquel Raynal <miquel.raynal@bootlin.com
|
||||
*/
|
||||
|
||||
+#include <linux/ctype.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
+#include <linux/etherdevice.h>
|
||||
+#include <linux/if_ether.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/of.h>
|
||||
@@ -21,6 +24,83 @@
|
||||
#define to_nvmem_layout_device(_dev) \
|
||||
container_of((_dev), struct nvmem_layout, dev)
|
||||
|
||||
+static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset,
|
||||
+ void *buf, size_t bytes)
|
||||
+{
|
||||
+ if (WARN_ON(bytes != ETH_ALEN))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (index)
|
||||
+ eth_addr_add(buf, index);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset,
|
||||
+ void *buf, size_t bytes)
|
||||
+{
|
||||
+ u8 mac[ETH_ALEN];
|
||||
+
|
||||
+ if (WARN_ON(bytes != 3 * ETH_ALEN - 1))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!mac_pton(buf, mac))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (index)
|
||||
+ eth_addr_add(mac, index);
|
||||
+
|
||||
+ ether_addr_copy(buf, mac);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset,
|
||||
+ void *buf, size_t bytes)
|
||||
+{
|
||||
+ u8 mac[ETH_ALEN], *hexstr;
|
||||
+ int i;
|
||||
+
|
||||
+ if (WARN_ON(bytes != 2 * ETH_ALEN))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ hexstr = (u8 *)buf;
|
||||
+ for (i = 0; i < ETH_ALEN; i++) {
|
||||
+ if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1]))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]);
|
||||
+ }
|
||||
+
|
||||
+ if (index)
|
||||
+ eth_addr_add(mac, index);
|
||||
+
|
||||
+ ether_addr_copy(buf, mac);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info)
|
||||
+{
|
||||
+ if (!of_device_is_compatible(info->np, "mac-base"))
|
||||
+ return;
|
||||
+
|
||||
+ if (info->bytes == ETH_ALEN) {
|
||||
+ info->raw_len = info->bytes;
|
||||
+ info->bytes = ETH_ALEN;
|
||||
+ info->read_post_process = nvmem_mac_base_raw_read;
|
||||
+ } else if (info->bytes == 2 * ETH_ALEN) {
|
||||
+ info->raw_len = info->bytes;
|
||||
+ info->bytes = ETH_ALEN;
|
||||
+ info->read_post_process = nvmem_mac_base_hex_read;
|
||||
+ } else if (info->bytes == 3 * ETH_ALEN - 1) {
|
||||
+ info->raw_len = info->bytes;
|
||||
+ info->bytes = ETH_ALEN;
|
||||
+ info->read_post_process = nvmem_mac_base_ascii_read;
|
||||
+ }
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(nvmem_layout_parse_mac_base);
|
||||
+
|
||||
static int nvmem_layout_bus_match(struct device *dev, const struct device_driver *drv)
|
||||
{
|
||||
return of_driver_match_device(dev, drv);
|
||||
--- a/include/linux/nvmem-provider.h
|
||||
+++ b/include/linux/nvmem-provider.h
|
||||
@@ -242,6 +242,8 @@ static inline void nvmem_layout_unregist
|
||||
|
||||
#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
|
||||
|
||||
+void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info);
|
||||
+
|
||||
/**
|
||||
* of_nvmem_layout_get_container() - Get OF node of layout container
|
||||
*
|
||||
@@ -254,6 +256,8 @@ struct device_node *of_nvmem_layout_get_
|
||||
|
||||
#else /* CONFIG_NVMEM && CONFIG_OF */
|
||||
|
||||
+static inline void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info) {}
|
||||
+
|
||||
static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
|
||||
{
|
||||
return NULL;
|
|
@ -0,0 +1,204 @@
|
|||
From 38287e8ec5c0281377fc70f11f20bcd9986a05f5 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 3 Feb 2025 00:36:12 +0100
|
||||
Subject: [PATCH 2/2] nvmem: layouts: add support for ascii-env driver
|
||||
|
||||
Add support for simple ASCII envirorment driver for NVMEM layouts.
|
||||
|
||||
It's very common for devices to store simple text file format in
|
||||
partition for environment varibles. The most common pattern is variable
|
||||
name, a delimiter and variable value all separated by a new line
|
||||
character (\n).
|
||||
|
||||
This driver adds support for exporting such data and expose NVMEM cells
|
||||
so they can be referenced by other drivers. This driver also supports
|
||||
parsing mac-base NVMEM cells to parse ASCII or HEX mac address.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/nvmem/layouts/Kconfig | 13 +++
|
||||
drivers/nvmem/layouts/Makefile | 1 +
|
||||
drivers/nvmem/layouts/ascii-env.c | 140 ++++++++++++++++++++++++++++++
|
||||
3 files changed, 154 insertions(+)
|
||||
create mode 100644 drivers/nvmem/layouts/ascii-env.c
|
||||
|
||||
--- a/drivers/nvmem/layouts/Kconfig
|
||||
+++ b/drivers/nvmem/layouts/Kconfig
|
||||
@@ -37,6 +37,19 @@ config NVMEM_LAYOUT_U_BOOT_ENV
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
+config NVMEM_LAYOUT_ASCII_ENV
|
||||
+ tristate "ASCII environment variables layout"
|
||||
+ help
|
||||
+ It's very common for devices to store simple text file format in
|
||||
+ partition for environment varibles. The most common pattern is variable
|
||||
+ name, a delimiter and variable value all separated by a new line
|
||||
+ character (\n).
|
||||
+ This driver adds support for exporting such data and expose NVMEM cells
|
||||
+ so they can be referenced by other drivers. This driver also supports
|
||||
+ parsing mac-base NVMEM cells to parse ASCII or HEX mac address.
|
||||
+
|
||||
+ If unsure, say N.
|
||||
+
|
||||
endmenu
|
||||
|
||||
endif
|
||||
--- a/drivers/nvmem/layouts/Makefile
|
||||
+++ b/drivers/nvmem/layouts/Makefile
|
||||
@@ -6,3 +6,4 @@
|
||||
obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
|
||||
obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
|
||||
obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
|
||||
+obj-$(CONFIG_NVMEM_LAYOUT_ASCII_ENV) += ascii-env.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/nvmem/layouts/ascii-env.c
|
||||
@@ -0,0 +1,148 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * Copyright (C) 2024 Christian Marangi <ansuelsmth@gmail.com>
|
||||
+ *
|
||||
+ * This borrow some parse logic from u-boot-env.
|
||||
+ */
|
||||
+#include <linux/nvmem-consumer.h>
|
||||
+#include <linux/nvmem-provider.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+struct ascii_env_match_data {
|
||||
+ const char delim;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Parse a buffer as an ASCII text with name delimiter value and each pattern separated
|
||||
+ * with a new line char '\n'
|
||||
+ * Example: (delimiter '=')
|
||||
+ * name=value\nname2=value2\n
|
||||
+ * 2 Cell:
|
||||
+ * - name: value
|
||||
+ * - name2: value2
|
||||
+ */
|
||||
+static int ascii_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
|
||||
+ size_t data_len, const char delim)
|
||||
+{
|
||||
+ char *var, *value, *eq, *lf;
|
||||
+ char *data = buf;
|
||||
+
|
||||
+ /*
|
||||
+ * Warning the inner loop take care of replacing '\n'
|
||||
+ * with '\0', hence we can use strlen on value.
|
||||
+ */
|
||||
+ for (var = data; var < data + data_len && *var;
|
||||
+ var = value + strlen(value) + 1) {
|
||||
+ struct nvmem_cell_info info = {};
|
||||
+ struct device_node *child;
|
||||
+ const char *label;
|
||||
+
|
||||
+ eq = strchr(var, delim);
|
||||
+ if (!eq)
|
||||
+ break;
|
||||
+ *eq = '\0';
|
||||
+ value = eq + 1;
|
||||
+
|
||||
+ /* Replace '\n' with '\0' to use strlen for value */
|
||||
+ lf = strchr(value, '\n');
|
||||
+ if (!lf)
|
||||
+ break;
|
||||
+ *lf = '\0';
|
||||
+
|
||||
+ info.name = devm_kstrdup(dev, var, GFP_KERNEL);
|
||||
+ if (!info.name)
|
||||
+ return -ENOMEM;
|
||||
+ info.offset = value - data;
|
||||
+ info.bytes = strlen(value);
|
||||
+ info.np = of_get_child_by_name(dev->of_node, info.name);
|
||||
+ for_each_child_of_node(dev->of_node, child) {
|
||||
+ if (!of_property_read_string(child, "label", &label) &&
|
||||
+ !strncmp(info.name, label, info.bytes))
|
||||
+ info.np = child;
|
||||
+ else if (of_node_name_eq(child, info.name))
|
||||
+ info.np = child;
|
||||
+ }
|
||||
+
|
||||
+ nvmem_layout_parse_mac_base(&info);
|
||||
+
|
||||
+ nvmem_add_one_cell(nvmem, &info);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ascii_env_add_cells(struct nvmem_layout *layout)
|
||||
+{
|
||||
+ struct nvmem_device *nvmem = layout->nvmem;
|
||||
+ const struct ascii_env_match_data *data;
|
||||
+ struct device *dev = &layout->dev;
|
||||
+ size_t dev_size;
|
||||
+ uint8_t *buf;
|
||||
+ int bytes;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Get the delimiter for name value pattern */
|
||||
+ data = device_get_match_data(dev);
|
||||
+
|
||||
+ dev_size = nvmem_dev_size(nvmem);
|
||||
+
|
||||
+ buf = kzalloc(dev_size, GFP_KERNEL);
|
||||
+ if (!buf) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_out;
|
||||
+ }
|
||||
+
|
||||
+ bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
|
||||
+ if (bytes < 0) {
|
||||
+ ret = bytes;
|
||||
+ goto err_kfree;
|
||||
+ } else if (bytes != dev_size) {
|
||||
+ ret = -EIO;
|
||||
+ goto err_kfree;
|
||||
+ }
|
||||
+
|
||||
+ buf[dev_size - 1] = '\0';
|
||||
+ ret = ascii_env_parse_cells(dev, nvmem, buf, dev_size, data->delim);
|
||||
+
|
||||
+err_kfree:
|
||||
+ kfree(buf);
|
||||
+err_out:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int ascii_env_probe(struct nvmem_layout *layout)
|
||||
+{
|
||||
+ layout->add_cells = ascii_env_add_cells;
|
||||
+
|
||||
+ return nvmem_layout_register(layout);
|
||||
+}
|
||||
+
|
||||
+static void ascii_env_remove(struct nvmem_layout *layout)
|
||||
+{
|
||||
+ nvmem_layout_unregister(layout);
|
||||
+}
|
||||
+
|
||||
+static const struct ascii_env_match_data ascii_env_eq = {
|
||||
+ .delim = '=',
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id ascii_env_of_match_table[] = {
|
||||
+ { .compatible = "ascii-eq-delim-env", .data = &ascii_env_eq, },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
+static struct nvmem_layout_driver ascii_env_layout = {
|
||||
+ .driver = {
|
||||
+ .name = "ascii-env-layout",
|
||||
+ .of_match_table = ascii_env_of_match_table,
|
||||
+ },
|
||||
+ .probe = ascii_env_probe,
|
||||
+ .remove = ascii_env_remove,
|
||||
+};
|
||||
+module_nvmem_layout_driver(ascii_env_layout);
|
||||
+
|
||||
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_DEVICE_TABLE(of, ascii_env_of_match_table);
|
||||
+MODULE_DESCRIPTION("NVMEM layout driver for ASCII environment variables");
|
|
@ -0,0 +1,181 @@
|
|||
From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
|
||||
To: Pavel Machek <pavel@ucw.cz>, Lee Jones <lee@kernel.org>,
|
||||
Rob Herring <robh@kernel.org>,
|
||||
Krzysztof Kozlowski <krzk+dt@kernel.org>,
|
||||
Conor Dooley <conor+dt@kernel.org>,
|
||||
Jonathan Corbet <corbet@lwn.net>,
|
||||
Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>,
|
||||
linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
|
||||
linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org
|
||||
Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
Subject: [PATCH v11 2/3] dt-bindings: leds: Add LED1202 LED Controller
|
||||
Date: Wed, 18 Dec 2024 18:33:58 +0000 [thread overview]
|
||||
Message-ID: <20241218183401.41687-3-vicentiu.galanopulo@remote-tech.co.uk> (raw)
|
||||
In-Reply-To: <20241218183401.41687-1-vicentiu.galanopulo@remote-tech.co.uk>
|
||||
|
||||
The LED1202 is a 12-channel low quiescent current LED driver with:
|
||||
* Supply range from 2.6 V to 5 V
|
||||
* 20 mA current capability per channel
|
||||
* 1.8 V compatible I2C control interface
|
||||
* 8-bit analog dimming individual control
|
||||
* 12-bit local PWM resolution
|
||||
* 8 programmable patterns
|
||||
|
||||
If the led node is present in the controller then the channel is
|
||||
set to active.
|
||||
|
||||
Signed-off-by: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
|
||||
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
---
|
||||
v1: https://lore.kernel.org/lkml/ZnCnnQfwuRueCIQ0@admins-Air/T/
|
||||
v2: https://lore.kernel.org/all/ZniNdGgKyUMV-hjq@admins-Air/T/
|
||||
v3: https://lore.kernel.org/all/ZniNdGgKyUMV-hjq@admins-Air/T/
|
||||
|
||||
Changes in v4:
|
||||
- remove label property, use devm_led_classdev_register_ext instead
|
||||
Changes in v3:
|
||||
- remove active property
|
||||
Changes in v2:
|
||||
- renamed label to remove color from it
|
||||
- add color property for each node
|
||||
- add function and function-enumerator property for each node
|
||||
|
||||
.../devicetree/bindings/leds/st,led1202.yaml | 132 ++++++++++++++++++
|
||||
1 file changed, 132 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/leds/st,led1202.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/leds/st,led1202.yaml
|
||||
@@ -0,0 +1,132 @@
|
||||
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/leds/st,led1202.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: ST LED1202 LED controllers
|
||||
+
|
||||
+maintainers:
|
||||
+ - Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
|
||||
+
|
||||
+description: |
|
||||
+ The LED1202 is a 12-channel low quiescent current LED controller
|
||||
+ programmable via I2C; The output current can be adjusted separately
|
||||
+ for each channel by 8-bit analog and 12-bit digital dimming control.
|
||||
+ Datasheet available at
|
||||
+ https://www.st.com/en/power-management/led1202.html
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ const: st,led1202
|
||||
+
|
||||
+ reg:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ "#address-cells":
|
||||
+ const: 1
|
||||
+
|
||||
+ "#size-cells":
|
||||
+ const: 0
|
||||
+
|
||||
+patternProperties:
|
||||
+ "^led@[0-9a-f]$":
|
||||
+ type: object
|
||||
+ $ref: common.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ properties:
|
||||
+ reg:
|
||||
+ minimum: 0
|
||||
+ maximum: 11
|
||||
+
|
||||
+ required:
|
||||
+ - reg
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+ - "#address-cells"
|
||||
+ - "#size-cells"
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ #include <dt-bindings/leds/common.h>
|
||||
+
|
||||
+ i2c {
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ led-controller@58 {
|
||||
+ compatible = "st,led1202";
|
||||
+ reg = <0x58>;
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ led@0 {
|
||||
+ reg = <0x0>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_RED>;
|
||||
+ function-enumerator = <1>;
|
||||
+ };
|
||||
+
|
||||
+ led@1 {
|
||||
+ reg = <0x1>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_GREEN>;
|
||||
+ function-enumerator = <2>;
|
||||
+ };
|
||||
+
|
||||
+ led@2 {
|
||||
+ reg = <0x2>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_BLUE>;
|
||||
+ function-enumerator = <3>;
|
||||
+ };
|
||||
+
|
||||
+ led@3 {
|
||||
+ reg = <0x3>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_RED>;
|
||||
+ function-enumerator = <4>;
|
||||
+ };
|
||||
+
|
||||
+ led@4 {
|
||||
+ reg = <0x4>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_GREEN>;
|
||||
+ function-enumerator = <5>;
|
||||
+ };
|
||||
+
|
||||
+ led@5 {
|
||||
+ reg = <0x5>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_BLUE>;
|
||||
+ function-enumerator = <6>;
|
||||
+ };
|
||||
+
|
||||
+ led@6 {
|
||||
+ reg = <0x6>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_RED>;
|
||||
+ function-enumerator = <7>;
|
||||
+ };
|
||||
+
|
||||
+ led@7 {
|
||||
+ reg = <0x7>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_GREEN>;
|
||||
+ function-enumerator = <8>;
|
||||
+ };
|
||||
+
|
||||
+ led@8 {
|
||||
+ reg = <0x8>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ color = <LED_COLOR_ID_BLUE>;
|
||||
+ function-enumerator = <9>;
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+...
|
|
@ -0,0 +1,513 @@
|
|||
From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
|
||||
To: Pavel Machek <pavel@ucw.cz>, Lee Jones <lee@kernel.org>,
|
||||
Rob Herring <robh@kernel.org>,
|
||||
Krzysztof Kozlowski <krzk+dt@kernel.org>,
|
||||
Conor Dooley <conor+dt@kernel.org>,
|
||||
Jonathan Corbet <corbet@lwn.net>,
|
||||
Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>,
|
||||
linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
|
||||
linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org
|
||||
Subject: [PATCH v11 3/3] leds: Add LED1202 I2C driver
|
||||
Date: Wed, 18 Dec 2024 18:33:59 +0000 [thread overview]
|
||||
Message-ID: <20241218183401.41687-4-vicentiu.galanopulo@remote-tech.co.uk> (raw)
|
||||
In-Reply-To: <20241218183401.41687-1-vicentiu.galanopulo@remote-tech.co.uk>
|
||||
|
||||
The output current can be adjusted separately for each channel by 8-bit
|
||||
analog (current sink input) and 12-bit digital (PWM) dimming control. The
|
||||
LED1202 implements 12 low-side current generators with independent dimming
|
||||
control.
|
||||
Internal volatile memory allows the user to store up to 8 different patterns,
|
||||
each pattern is a particular output configuration in terms of PWM
|
||||
duty-cycle (on 4096 steps). Analog dimming (on 256 steps) is per channel but
|
||||
common to all patterns. Each device tree LED node will have a corresponding
|
||||
entry in /sys/class/leds with the label name. The brightness property
|
||||
corresponds to the per channel analog dimming, while the patterns[1-8] to the
|
||||
PWM dimming control.
|
||||
|
||||
Signed-off-by: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
|
||||
---
|
||||
Changes in v10:
|
||||
- update description help in Kconfig
|
||||
- move st1202_led and st1202_chip into one line, declaration and definition
|
||||
Changes in v9:
|
||||
- log errors directly in st1202_write_reg and st1202_read_reg
|
||||
- use mutex guards instead of lock/unlock
|
||||
- remove i2c_set_clientdata
|
||||
Changes in v7:
|
||||
- fix st1202_brightness_get() error: uninitialized symbol 'value'
|
||||
Changes in v6:
|
||||
- fix build error
|
||||
Changes in v5:
|
||||
- remove unused macros
|
||||
- switch to using devm_led_classdev_register_ext (struct st1202_led update)
|
||||
- add prescalar_to_milliseconds (convert [22..5660]ms to [0..255] reg value)
|
||||
- remove register range check in dt_init (range protected by yaml)
|
||||
- address all review comments in v4
|
||||
Changes in v4:
|
||||
- Remove attributes/extended attributes implementation
|
||||
- Use /sys/class/leds/<led>/hw_pattern (Pavel suggestion)
|
||||
- Implement review findings of Christophe JAILLET
|
||||
Changes in v3:
|
||||
- Rename all ll1202 to st1202, including driver file name
|
||||
- Convert all magic numbers to defines
|
||||
- Refactor the show/store callbacks as per Lee's and Thomas's review
|
||||
- Remove ll1202_get_channel and use dev_ext_attributes instead
|
||||
- Log all error values for all the functions
|
||||
- Use sysfs_emit for show callbacks
|
||||
Changes in v2:
|
||||
- Fix build error for device_attribute modes
|
||||
|
||||
drivers/leds/Kconfig | 10 +
|
||||
drivers/leds/Makefile | 1 +
|
||||
drivers/leds/leds-st1202.c | 416 +++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 427 insertions(+)
|
||||
create mode 100644 drivers/leds/leds-st1202.c
|
||||
|
||||
--- a/drivers/leds/Kconfig
|
||||
+++ b/drivers/leds/Kconfig
|
||||
@@ -931,6 +931,16 @@ config LEDS_LM36274
|
||||
Say Y to enable the LM36274 LED driver for TI LMU devices.
|
||||
This supports the LED device LM36274.
|
||||
|
||||
+config LEDS_ST1202
|
||||
+ tristate "LED Support for STMicroelectronics LED1202 I2C chips"
|
||||
+ depends on LEDS_CLASS
|
||||
+ depends on I2C
|
||||
+ depends on OF
|
||||
+ select LEDS_TRIGGERS
|
||||
+ help
|
||||
+ Say Y to enable support for LEDs connected to LED1202
|
||||
+ LED driver chips accessed via the I2C bus.
|
||||
+
|
||||
config LEDS_TPS6105X
|
||||
tristate "LED support for TI TPS6105X"
|
||||
depends on LEDS_CLASS
|
||||
--- a/drivers/leds/Makefile
|
||||
+++ b/drivers/leds/Makefile
|
||||
@@ -82,6 +82,7 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
|
||||
obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
|
||||
obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
|
||||
obj-$(CONFIG_LEDS_SUN50I_A100) += leds-sun50i-a100.o
|
||||
+obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o
|
||||
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
|
||||
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
|
||||
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/leds/leds-st1202.c
|
||||
@@ -0,0 +1,416 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * LED driver for STMicroelectronics LED1202 chip
|
||||
+ *
|
||||
+ * Copyright (C) 2024 Remote-Tech Ltd. UK
|
||||
+ */
|
||||
+
|
||||
+#include <linux/cleanup.h>
|
||||
+#include <linux/ctype.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/gpio.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/leds.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/string.h>
|
||||
+
|
||||
+#define ST1202_CHAN_DISABLE_ALL 0x00
|
||||
+#define ST1202_CHAN_ENABLE_HIGH 0x03
|
||||
+#define ST1202_CHAN_ENABLE_LOW 0x02
|
||||
+#define ST1202_CONFIG_REG 0x04
|
||||
+/* PATS: Pattern sequence feature enable */
|
||||
+#define ST1202_CONFIG_REG_PATS BIT(7)
|
||||
+/* PATSR: Pattern sequence runs (self-clear when sequence is finished) */
|
||||
+#define ST1202_CONFIG_REG_PATSR BIT(6)
|
||||
+#define ST1202_CONFIG_REG_SHFT BIT(3)
|
||||
+#define ST1202_DEV_ENABLE 0x01
|
||||
+#define ST1202_DEV_ENABLE_ON BIT(0)
|
||||
+#define ST1202_DEV_ENABLE_RESET BIT(7)
|
||||
+#define ST1202_DEVICE_ID 0x00
|
||||
+#define ST1202_ILED_REG0 0x09
|
||||
+#define ST1202_MAX_LEDS 12
|
||||
+#define ST1202_MAX_PATTERNS 8
|
||||
+#define ST1202_MILLIS_PATTERN_DUR_MAX 5660
|
||||
+#define ST1202_MILLIS_PATTERN_DUR_MIN 22
|
||||
+#define ST1202_PATTERN_DUR 0x16
|
||||
+#define ST1202_PATTERN_PWM 0x1E
|
||||
+#define ST1202_PATTERN_REP 0x15
|
||||
+
|
||||
+struct st1202_led {
|
||||
+ struct fwnode_handle *fwnode;
|
||||
+ struct led_classdev led_cdev;
|
||||
+ struct st1202_chip *chip;
|
||||
+ bool is_active;
|
||||
+ int led_num;
|
||||
+};
|
||||
+
|
||||
+struct st1202_chip {
|
||||
+ struct i2c_client *client;
|
||||
+ struct mutex lock;
|
||||
+ struct st1202_led leds[ST1202_MAX_LEDS];
|
||||
+};
|
||||
+
|
||||
+static struct st1202_led *cdev_to_st1202_led(struct led_classdev *cdev)
|
||||
+{
|
||||
+ return container_of(cdev, struct st1202_led, led_cdev);
|
||||
+}
|
||||
+
|
||||
+static int st1202_read_reg(struct st1202_chip *chip, int reg, uint8_t *val)
|
||||
+{
|
||||
+ struct device *dev = &chip->client->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(chip->client, reg);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(dev, "Failed to read register [0x%x]: %d\n", reg, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ *val = (uint8_t)ret;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val)
|
||||
+{
|
||||
+ struct device *dev = &chip->client->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = i2c_smbus_write_byte_data(chip->client, reg, val);
|
||||
+ if (ret != 0)
|
||||
+ dev_err(dev, "Failed to write %d to register [0x%x]: %d\n", val, reg, ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static uint8_t st1202_prescalar_to_miliseconds(unsigned int value)
|
||||
+{
|
||||
+ return value / ST1202_MILLIS_PATTERN_DUR_MIN - 1;
|
||||
+}
|
||||
+
|
||||
+static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num,
|
||||
+ int pattern, unsigned int value)
|
||||
+{
|
||||
+ u8 value_l, value_h;
|
||||
+ int ret;
|
||||
+
|
||||
+ value_l = (u8)value;
|
||||
+ value_h = (u8)(value >> 8);
|
||||
+
|
||||
+ /*
|
||||
+ * Datasheet: Register address low = 1Eh + 2*(xh) + 18h*(yh),
|
||||
+ * where x is the channel number (led number) in hexadecimal (x = 00h .. 0Bh)
|
||||
+ * and y is the pattern number in hexadecimal (y = 00h .. 07h)
|
||||
+ */
|
||||
+ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + (led_num * 2) + 0x18 * pattern),
|
||||
+ value_l);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /*
|
||||
+ * Datasheet: Register address high = 1Eh + 01h + 2(xh) +18h*(yh),
|
||||
+ * where x is the channel number in hexadecimal (x = 00h .. 0Bh)
|
||||
+ * and y is the pattern number in hexadecimal (y = 00h .. 07h)
|
||||
+ */
|
||||
+ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + 0x1 + (led_num * 2) + 0x18 * pattern),
|
||||
+ value_h);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern,
|
||||
+ unsigned int value)
|
||||
+{
|
||||
+ return st1202_write_reg(chip, (ST1202_PATTERN_DUR + pattern),
|
||||
+ st1202_prescalar_to_miliseconds(value));
|
||||
+}
|
||||
+
|
||||
+static void st1202_brightness_set(struct led_classdev *led_cdev,
|
||||
+ enum led_brightness value)
|
||||
+{
|
||||
+ struct st1202_led *led = cdev_to_st1202_led(led_cdev);
|
||||
+ struct st1202_chip *chip = led->chip;
|
||||
+
|
||||
+ guard(mutex)(&chip->lock);
|
||||
+
|
||||
+ st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
|
||||
+}
|
||||
+
|
||||
+static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
|
||||
+{
|
||||
+ struct st1202_led *led = cdev_to_st1202_led(led_cdev);
|
||||
+ struct st1202_chip *chip = led->chip;
|
||||
+ u8 value = 0;
|
||||
+
|
||||
+ guard(mutex)(&chip->lock);
|
||||
+
|
||||
+ st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value);
|
||||
+
|
||||
+ return value;
|
||||
+}
|
||||
+
|
||||
+static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
|
||||
+{
|
||||
+ u8 chan_low, chan_high;
|
||||
+ int ret;
|
||||
+
|
||||
+ guard(mutex)(&chip->lock);
|
||||
+
|
||||
+ if (led_num <= 7) {
|
||||
+ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_LOW, &chan_low);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ chan_low = active ? chan_low | BIT(led_num) : chan_low & ~BIT(led_num);
|
||||
+
|
||||
+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, chan_low);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ } else {
|
||||
+ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_HIGH, &chan_high);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ chan_high = active ? chan_high | (BIT(led_num) >> 8) :
|
||||
+ chan_high & ~(BIT(led_num) >> 8);
|
||||
+
|
||||
+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, chan_high);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value)
|
||||
+{
|
||||
+ struct st1202_led *led = cdev_to_st1202_led(ldev);
|
||||
+ struct st1202_chip *chip = led->chip;
|
||||
+
|
||||
+ return st1202_channel_set(chip, led->led_num, value == LED_OFF ? false : true);
|
||||
+}
|
||||
+
|
||||
+static int st1202_led_pattern_clear(struct led_classdev *ldev)
|
||||
+{
|
||||
+ struct st1202_led *led = cdev_to_st1202_led(ldev);
|
||||
+ struct st1202_chip *chip = led->chip;
|
||||
+ int ret;
|
||||
+
|
||||
+ guard(mutex)(&chip->lock);
|
||||
+
|
||||
+ for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
|
||||
+ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = st1202_duration_pattern_write(chip, patt, ST1202_MILLIS_PATTERN_DUR_MIN);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int st1202_led_pattern_set(struct led_classdev *ldev,
|
||||
+ struct led_pattern *pattern,
|
||||
+ u32 len, int repeat)
|
||||
+{
|
||||
+ struct st1202_led *led = cdev_to_st1202_led(ldev);
|
||||
+ struct st1202_chip *chip = led->chip;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (len > ST1202_MAX_PATTERNS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ guard(mutex)(&chip->lock);
|
||||
+
|
||||
+ for (int patt = 0; patt < len; patt++) {
|
||||
+ if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
|
||||
+ pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = st1202_duration_pattern_write(chip, patt, pattern[patt].delta_t);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = st1202_write_reg(chip, ST1202_PATTERN_REP, repeat);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, (ST1202_CONFIG_REG_PATSR |
|
||||
+ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_SHFT));
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int st1202_dt_init(struct st1202_chip *chip)
|
||||
+{
|
||||
+ struct device *dev = &chip->client->dev;
|
||||
+ struct st1202_led *led;
|
||||
+ int err, reg;
|
||||
+
|
||||
+ for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
|
||||
+ struct led_init_data init_data = {};
|
||||
+
|
||||
+ err = of_property_read_u32(child, "reg", ®);
|
||||
+ if (err)
|
||||
+ return dev_err_probe(dev, err, "Invalid register\n");
|
||||
+
|
||||
+ led = &chip->leds[reg];
|
||||
+ led->is_active = true;
|
||||
+ led->fwnode = of_fwnode_handle(child);
|
||||
+
|
||||
+ led->led_cdev.max_brightness = U8_MAX;
|
||||
+ led->led_cdev.brightness_set_blocking = st1202_led_set;
|
||||
+ led->led_cdev.pattern_set = st1202_led_pattern_set;
|
||||
+ led->led_cdev.pattern_clear = st1202_led_pattern_clear;
|
||||
+ led->led_cdev.default_trigger = "pattern";
|
||||
+
|
||||
+ init_data.fwnode = led->fwnode;
|
||||
+ init_data.devicename = "st1202";
|
||||
+ init_data.default_label = ":";
|
||||
+
|
||||
+ err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
|
||||
+ if (err < 0)
|
||||
+ return dev_err_probe(dev, err, "Failed to register LED class device\n");
|
||||
+
|
||||
+ led->led_cdev.brightness_set = st1202_brightness_set;
|
||||
+ led->led_cdev.brightness_get = st1202_brightness_get;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int st1202_setup(struct st1202_chip *chip)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ guard(mutex)(&chip->lock);
|
||||
+
|
||||
+ /*
|
||||
+ * Once the supply voltage is applied, the LED1202 executes some internal checks,
|
||||
+ * afterwords it stops the oscillator and puts the internal LDO in quiescent mode.
|
||||
+ * To start the device, EN bit must be set inside the “Device Enable” register at
|
||||
+ * address 01h. As soon as EN is set, the LED1202 loads the adjustment parameters
|
||||
+ * from the internal non-volatile memory and performs an auto-calibration procedure
|
||||
+ * in order to increase the output current precision.
|
||||
+ * Such initialization lasts about 6.5 ms.
|
||||
+ */
|
||||
+
|
||||
+ /* Reset the chip during setup */
|
||||
+ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_RESET);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Enable phase-shift delay feature */
|
||||
+ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Enable the device */
|
||||
+ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_ON);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Duration of initialization */
|
||||
+ usleep_range(6500, 10000);
|
||||
+
|
||||
+ /* Deactivate all LEDS (channels) and activate only the ones found in Device Tree */
|
||||
+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, ST1202_CHAN_DISABLE_ALL);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, ST1202_CHAN_DISABLE_ALL);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
|
||||
+ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_PATSR);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int st1202_probe(struct i2c_client *client)
|
||||
+{
|
||||
+ struct st1202_chip *chip;
|
||||
+ struct st1202_led *led;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
+ return dev_err_probe(&client->dev, -EIO, "SMBUS Byte Data not Supported\n");
|
||||
+
|
||||
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
+ if (!chip)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ devm_mutex_init(&client->dev, &chip->lock);
|
||||
+ chip->client = client;
|
||||
+
|
||||
+ ret = st1202_dt_init(chip);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = st1202_setup(chip);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ for (int i = 0; i < ST1202_MAX_LEDS; i++) {
|
||||
+ led = &chip->leds[i];
|
||||
+ led->chip = chip;
|
||||
+ led->led_num = i;
|
||||
+
|
||||
+ if (!led->is_active)
|
||||
+ continue;
|
||||
+
|
||||
+ ret = st1202_channel_set(led->chip, led->led_num, true);
|
||||
+ if (ret < 0)
|
||||
+ return dev_err_probe(&client->dev, ret,
|
||||
+ "Failed to activate LED channel\n");
|
||||
+
|
||||
+ ret = st1202_led_pattern_clear(&led->led_cdev);
|
||||
+ if (ret < 0)
|
||||
+ return dev_err_probe(&client->dev, ret,
|
||||
+ "Failed to clear LED pattern\n");
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct i2c_device_id st1202_id[] = {
|
||||
+ { "st1202-i2c" },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(i2c, st1202_id);
|
||||
+
|
||||
+static const struct of_device_id st1202_dt_ids[] = {
|
||||
+ { .compatible = "st,led1202" },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, st1202_dt_ids);
|
||||
+
|
||||
+static struct i2c_driver st1202_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "leds-st1202",
|
||||
+ .of_match_table = of_match_ptr(st1202_dt_ids),
|
||||
+ },
|
||||
+ .probe = st1202_probe,
|
||||
+ .id_table = st1202_id,
|
||||
+};
|
||||
+module_i2c_driver(st1202_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Remote Tech LTD");
|
||||
+MODULE_DESCRIPTION("STMicroelectronics LED1202 : 12-channel constant current LED driver");
|
||||
+MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,70 @@
|
|||
From e3da313ebcace17f1227566fe1b0d0c3883061f9 Mon Sep 17 00:00:00 2001
|
||||
From: Manuel Fombuena <fombuena@outlook.com>
|
||||
Date: Fri, 17 Jan 2025 12:31:49 +0000
|
||||
Subject: [PATCH 1/5] leds: leds-st1202: fix NULL pointer access on race
|
||||
condition
|
||||
|
||||
st1202_dt_init() calls devm_led_classdev_register_ext() before the
|
||||
internal data structures are properly setup, so the leds become visible
|
||||
to user space while being partially initialized, leading to a window
|
||||
where trying to access them causes a NULL pointer access.
|
||||
|
||||
This change moves devm_led_classdev_register_ext() to the last thing to
|
||||
happen during initialization to eliminate it.
|
||||
|
||||
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
|
||||
---
|
||||
drivers/leds/leds-st1202.c | 21 ++++++++++-----------
|
||||
1 file changed, 10 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/leds/leds-st1202.c
|
||||
+++ b/drivers/leds/leds-st1202.c
|
||||
@@ -261,8 +261,6 @@ static int st1202_dt_init(struct st1202_
|
||||
int err, reg;
|
||||
|
||||
for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
|
||||
- struct led_init_data init_data = {};
|
||||
-
|
||||
err = of_property_read_u32(child, "reg", ®);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "Invalid register\n");
|
||||
@@ -276,15 +274,6 @@ static int st1202_dt_init(struct st1202_
|
||||
led->led_cdev.pattern_set = st1202_led_pattern_set;
|
||||
led->led_cdev.pattern_clear = st1202_led_pattern_clear;
|
||||
led->led_cdev.default_trigger = "pattern";
|
||||
-
|
||||
- init_data.fwnode = led->fwnode;
|
||||
- init_data.devicename = "st1202";
|
||||
- init_data.default_label = ":";
|
||||
-
|
||||
- err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
|
||||
- if (err < 0)
|
||||
- return dev_err_probe(dev, err, "Failed to register LED class device\n");
|
||||
-
|
||||
led->led_cdev.brightness_set = st1202_brightness_set;
|
||||
led->led_cdev.brightness_get = st1202_brightness_get;
|
||||
}
|
||||
@@ -368,6 +357,7 @@ static int st1202_probe(struct i2c_clien
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < ST1202_MAX_LEDS; i++) {
|
||||
+ struct led_init_data init_data = {};
|
||||
led = &chip->leds[i];
|
||||
led->chip = chip;
|
||||
led->led_num = i;
|
||||
@@ -384,6 +374,15 @@ static int st1202_probe(struct i2c_clien
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Failed to clear LED pattern\n");
|
||||
+
|
||||
+ init_data.fwnode = led->fwnode;
|
||||
+ init_data.devicename = "st1202";
|
||||
+ init_data.default_label = ":";
|
||||
+
|
||||
+ ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data);
|
||||
+ if (ret < 0)
|
||||
+ return dev_err_probe(&client->dev, ret,
|
||||
+ "Failed to register LED class device\n");
|
||||
}
|
||||
|
||||
return 0;
|
Loading…
Add table
Add a link
Reference in a new issue