mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-31 02:17:58 +00:00 
			
		
		
		
	kernel: update patches
Signed-off-by: Arif Alam <arif.alam@netexperience.com>
This commit is contained in:
		| @@ -119,6 +119,7 @@ | ||||
|  	memory@40000000 { | ||||
| -		reg = <0 0x40000000 0 0x20000000>; | ||||
| +		reg = <0 0x40000000 0 0x40000000>; | ||||
| 		device_type = "memory"; | ||||
|  	}; | ||||
|   | ||||
|  	reg_1p8v: regulator-1p8v { | ||||
|   | ||||
| @@ -0,0 +1,31 @@ | ||||
| From b341f120cfc9ca1dfd48364b7f36ac2c1fbdea43 Mon Sep 17 00:00:00 2001 | ||||
| From: Xiangsheng Hou <xiangsheng.hou@mediatek.com> | ||||
| Date: Wed, 3 Apr 2019 16:30:01 +0800 | ||||
| Subject: [PATCH 3/6] mtd: spinand: disable on-die ECC | ||||
|  | ||||
| Change-Id: I9745adaed5295202fabbe8ab8947885c57a5b847 | ||||
| Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com> | ||||
| --- | ||||
|  drivers/mtd/nand/spi/core.c | 4 ++-- | ||||
|  1 file changed, 2 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/mtd/nand/spi/core.c | ||||
| +++ b/drivers/mtd/nand/spi/core.c | ||||
| @@ -491,7 +491,7 @@ static int spinand_mtd_read(struct mtd_i | ||||
|  	int ret = 0; | ||||
|   | ||||
|  	if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout) | ||||
| -		enable_ecc = true; | ||||
| +		enable_ecc = false; | ||||
|   | ||||
|  	mutex_lock(&spinand->lock); | ||||
|   | ||||
| @@ -539,7 +539,7 @@ static int spinand_mtd_write(struct mtd_ | ||||
|  	int ret = 0; | ||||
|   | ||||
|  	if (ops->mode != MTD_OPS_RAW && mtd->ooblayout) | ||||
| -		enable_ecc = true; | ||||
| +		enable_ecc = false; | ||||
|   | ||||
|  	mutex_lock(&spinand->lock); | ||||
|   | ||||
| @@ -389,25 +389,20 @@ Signed-off-by: chuanjia.liu <Chuanjia.Liu@mediatek.com> | ||||
|  					<0 0 0 2 &pcie_intc1 1>, | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts | ||||
| @@ -254,18 +254,16 @@ | ||||
| @@ -184,14 +184,16 @@ | ||||
|  	}; | ||||
|  }; | ||||
|   | ||||
| -&pcie { | ||||
| +&pcie0 { | ||||
|  	pinctrl-names = "default"; | ||||
| -	pinctrl-0 = <&pcie0_pins>, <&pcie1_pins>; | ||||
| +	pinctrl-0 = <&pcie0_pins>; | ||||
|  	pinctrl-0 = <&pcie0_pins>; | ||||
|  	status = "okay"; | ||||
| +}; | ||||
|   | ||||
| -	pcie@0,0 { | ||||
| -		status = "okay"; | ||||
| -	}; | ||||
| - | ||||
| -	pcie@1,0 { | ||||
| -		status = "okay"; | ||||
| -	}; | ||||
| +&pcie1 { | ||||
| +	pinctrl-names = "default"; | ||||
| +	pinctrl-0 = <&pcie1_pins>; | ||||
|   | ||||
| @@ -0,0 +1,980 @@ | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -919,6 +919,10 @@ struct xfrmdev_ops { | ||||
|  	bool	(*xdo_dev_offload_ok) (struct sk_buff *skb, | ||||
|  				       struct xfrm_state *x); | ||||
|  	void	(*xdo_dev_state_advance_esn) (struct xfrm_state *x); | ||||
| +	void	(*xdo_dev_state_update_curlft) (struct xfrm_state *x); | ||||
| +	int	(*xdo_dev_policy_add) (struct xfrm_policy *x); | ||||
| +	void	(*xdo_dev_policy_delete) (struct xfrm_policy *x); | ||||
| +	void	(*xdo_dev_policy_free) (struct xfrm_policy *x); | ||||
|  }; | ||||
|  #endif | ||||
|   | ||||
| --- a/include/net/xfrm.h | ||||
| +++ b/include/net/xfrm.h | ||||
| @@ -125,11 +125,25 @@ struct xfrm_state_walk { | ||||
|  	struct xfrm_address_filter *filter; | ||||
|  }; | ||||
|   | ||||
| +enum { | ||||
| +	XFRM_DEV_OFFLOAD_IN = 1, | ||||
| +	XFRM_DEV_OFFLOAD_OUT, | ||||
| +	XFRM_DEV_OFFLOAD_FWD, | ||||
| +}; | ||||
| + | ||||
| +enum { | ||||
| +	XFRM_DEV_OFFLOAD_UNSPECIFIED, | ||||
| +	XFRM_DEV_OFFLOAD_CRYPTO, | ||||
| +	XFRM_DEV_OFFLOAD_PACKET, | ||||
| +}; | ||||
| + | ||||
|  struct xfrm_state_offload { | ||||
|  	struct net_device	*dev; | ||||
|  	unsigned long		offload_handle; | ||||
|  	unsigned int		num_exthdrs; | ||||
|  	u8			flags; | ||||
| +	u8			dir : 2; | ||||
| +	u8			type : 2; | ||||
|  }; | ||||
|   | ||||
|  struct xfrm_mode { | ||||
| @@ -527,6 +541,8 @@ struct xfrm_policy { | ||||
|  	struct xfrm_tmpl       	xfrm_vec[XFRM_MAX_DEPTH]; | ||||
|  	struct hlist_node	bydst_inexact_list; | ||||
|  	struct rcu_head		rcu; | ||||
| + | ||||
| +	struct xfrm_state_offload xdo; | ||||
|  }; | ||||
|   | ||||
|  static inline struct net *xp_net(const struct xfrm_policy *xp) | ||||
| @@ -1084,6 +1100,29 @@ xfrm_state_addr_cmp(const struct xfrm_tm | ||||
|  } | ||||
|   | ||||
|  #ifdef CONFIG_XFRM | ||||
| +static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb) | ||||
| +{ | ||||
| +	struct sec_path *sp = skb_sec_path(skb); | ||||
| + | ||||
| +	return sp->xvec[sp->len - 1]; | ||||
| +} | ||||
| +#endif | ||||
| + | ||||
| +static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb) | ||||
| +{ | ||||
| +#ifdef CONFIG_XFRM | ||||
| +	struct sec_path *sp = skb_sec_path(skb); | ||||
| + | ||||
| +	if (!sp || !sp->olen || sp->len != sp->olen) | ||||
| +		return NULL; | ||||
| + | ||||
| +	return &sp->ovec[sp->olen - 1]; | ||||
| +#else | ||||
| +	return NULL; | ||||
| +#endif | ||||
| +} | ||||
| + | ||||
| +#ifdef CONFIG_XFRM | ||||
|  int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, | ||||
|  			unsigned short family); | ||||
|   | ||||
| @@ -1093,10 +1132,19 @@ static inline int __xfrm_policy_check2(s | ||||
|  { | ||||
|  	struct net *net = dev_net(skb->dev); | ||||
|  	int ndir = dir | (reverse ? XFRM_POLICY_MASK + 1 : 0); | ||||
| +	struct xfrm_offload *xo = xfrm_offload(skb); | ||||
| +	struct xfrm_state *x; | ||||
|   | ||||
|  	if (sk && sk->sk_policy[XFRM_POLICY_IN]) | ||||
|  		return __xfrm_policy_check(sk, ndir, skb, family); | ||||
|   | ||||
| +	if (xo) { | ||||
| +		x = xfrm_input_state(skb); | ||||
| +		if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) | ||||
| +			return (xo->flags & CRYPTO_DONE) && | ||||
| +			       (xo->status & CRYPTO_SUCCESS); | ||||
| +	} | ||||
| + | ||||
|  	return	(!net->xfrm.policy_count[dir] && !secpath_exists(skb)) || | ||||
|  		(skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) || | ||||
|  		__xfrm_policy_check(sk, ndir, skb, family); | ||||
| @@ -1490,6 +1538,23 @@ struct xfrm_state *xfrm_stateonly_find(s | ||||
|  struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi, | ||||
|  					      unsigned short family); | ||||
|  int xfrm_state_check_expire(struct xfrm_state *x); | ||||
| +#ifdef CONFIG_XFRM_OFFLOAD | ||||
| +static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x) | ||||
| +{ | ||||
| +	struct xfrm_state_offload *xdo = &x->xso; | ||||
| +	struct net_device *dev = xdo->dev; | ||||
| + | ||||
| +	if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
| +		return; | ||||
| + | ||||
| +	if (dev && dev->xfrmdev_ops && | ||||
| +	    dev->xfrmdev_ops->xdo_dev_state_update_curlft) | ||||
| +		dev->xfrmdev_ops->xdo_dev_state_update_curlft(x); | ||||
| + | ||||
| +} | ||||
| +#else | ||||
| +static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x) {} | ||||
| +#endif | ||||
|  void xfrm_state_insert(struct xfrm_state *x); | ||||
|  int xfrm_state_add(struct xfrm_state *x); | ||||
|  int xfrm_state_update(struct xfrm_state *x); | ||||
| @@ -1539,6 +1604,8 @@ struct xfrm_state *xfrm_find_acq_byseq(s | ||||
|  int xfrm_state_delete(struct xfrm_state *x); | ||||
|  int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync); | ||||
|  int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid); | ||||
| +int xfrm_dev_policy_flush(struct net *net, struct net_device *dev, | ||||
| +			  bool task_valid); | ||||
|  void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); | ||||
|  void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); | ||||
|  u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); | ||||
| @@ -1820,29 +1887,6 @@ static inline void xfrm_states_delete(st | ||||
|  } | ||||
|  #endif | ||||
|   | ||||
| -#ifdef CONFIG_XFRM | ||||
| -static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb) | ||||
| -{ | ||||
| -	struct sec_path *sp = skb_sec_path(skb); | ||||
| - | ||||
| -	return sp->xvec[sp->len - 1]; | ||||
| -} | ||||
| -#endif | ||||
| - | ||||
| -static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb) | ||||
| -{ | ||||
| -#ifdef CONFIG_XFRM | ||||
| -	struct sec_path *sp = skb_sec_path(skb); | ||||
| - | ||||
| -	if (!sp || !sp->olen || sp->len != sp->olen) | ||||
| -		return NULL; | ||||
| - | ||||
| -	return &sp->ovec[sp->olen - 1]; | ||||
| -#else | ||||
| -	return NULL; | ||||
| -#endif | ||||
| -} | ||||
| - | ||||
|  void __init xfrm_dev_init(void); | ||||
|   | ||||
|  #ifdef CONFIG_XFRM_OFFLOAD | ||||
| @@ -1851,6 +1895,9 @@ void xfrm_dev_backlog(struct softnet_dat | ||||
|  struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again); | ||||
|  int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, | ||||
|  		       struct xfrm_user_offload *xuo); | ||||
| +int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, | ||||
| +			struct xfrm_user_offload *xuo, u8 dir, | ||||
| +			struct netlink_ext_ack *extack); | ||||
|  bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x); | ||||
|   | ||||
|  static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x) | ||||
| @@ -1899,6 +1946,27 @@ static inline void xfrm_dev_state_free(s | ||||
|  		dev_put(dev); | ||||
|  	} | ||||
|  } | ||||
| + | ||||
| +static inline void xfrm_dev_policy_delete(struct xfrm_policy *x) | ||||
| +{ | ||||
| +	struct xfrm_state_offload *xdo = &x->xdo; | ||||
| +	struct net_device *dev = xdo->dev; | ||||
| + | ||||
| +	if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_policy_delete) | ||||
| +		dev->xfrmdev_ops->xdo_dev_policy_delete(x); | ||||
| +} | ||||
| + | ||||
| +static inline void xfrm_dev_policy_free(struct xfrm_policy *x) | ||||
| +{ | ||||
| +	struct xfrm_state_offload *xdo = &x->xdo; | ||||
| +	struct net_device *dev = xdo->dev; | ||||
| + | ||||
| +	if (dev && dev->xfrmdev_ops) { | ||||
| +		if (dev->xfrmdev_ops->xdo_dev_policy_free) | ||||
| +			dev->xfrmdev_ops->xdo_dev_policy_free(x); | ||||
| +		xdo->dev = NULL; | ||||
| +	} | ||||
| +} | ||||
|  #else | ||||
|  static inline void xfrm_dev_resume(struct sk_buff *skb) | ||||
|  { | ||||
| @@ -1931,6 +1999,21 @@ static inline bool xfrm_dev_offload_ok(s | ||||
|  	return false; | ||||
|  } | ||||
|   | ||||
| +static inline int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, | ||||
| +				      struct xfrm_user_offload *xuo, u8 dir, | ||||
| +				      struct netlink_ext_ack *extack) | ||||
| +{ | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static inline void xfrm_dev_policy_delete(struct xfrm_policy *x) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
| +static inline void xfrm_dev_policy_free(struct xfrm_policy *x) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
|  static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x) | ||||
|  { | ||||
|  } | ||||
| --- a/include/uapi/linux/xfrm.h | ||||
| +++ b/include/uapi/linux/xfrm.h | ||||
| @@ -512,6 +512,12 @@ struct xfrm_user_offload { | ||||
|   */ | ||||
|  #define XFRM_OFFLOAD_IPV6	1 | ||||
|  #define XFRM_OFFLOAD_INBOUND	2 | ||||
| +/* Two bits above are relevant for state path only, while | ||||
| + * offload is used for both policy and state flows. | ||||
| + * | ||||
| + * In policy offload mode, they are free and can be safely reused. | ||||
| + */ | ||||
| +#define XFRM_OFFLOAD_PACKET	4 | ||||
|   | ||||
|  #ifndef __KERNEL__ | ||||
|  /* backwards compatibility for userspace */ | ||||
| --- a/net/xfrm/xfrm_device.c | ||||
| +++ b/net/xfrm/xfrm_device.c | ||||
| @@ -80,6 +80,7 @@ struct sk_buff *validate_xmit_xfrm(struc | ||||
|  	struct softnet_data *sd; | ||||
|  	netdev_features_t esp_features = features; | ||||
|  	struct xfrm_offload *xo = xfrm_offload(skb); | ||||
| +	struct net_device *dev = skb->dev; | ||||
|  	struct sec_path *sp; | ||||
|   | ||||
|  	if (!xo || (xo->flags & XFRM_XMIT)) | ||||
| @@ -93,6 +94,17 @@ struct sk_buff *validate_xmit_xfrm(struc | ||||
|  	if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND) | ||||
|  		return skb; | ||||
|   | ||||
| +	/* The packet was sent to HW IPsec packet offload engine, | ||||
| +	 * but to wrong device. Drop the packet, so it won't skip | ||||
| +	 * XFRM stack. | ||||
| +	 */ | ||||
| +	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET && x->xso.dev != dev) { | ||||
| +		kfree_skb(skb); | ||||
| +		//dev_core_stats_tx_dropped_inc(dev); | ||||
| +		atomic_long_inc(&dev->tx_dropped); | ||||
| +		return NULL; | ||||
| +	} | ||||
| + | ||||
|  	local_irq_save(flags); | ||||
|  	sd = this_cpu_ptr(&softnet_data); | ||||
|  	err = !skb_queue_empty(&sd->xfrm_backlog); | ||||
| @@ -198,6 +210,7 @@ int xfrm_dev_state_add(struct net *net, | ||||
|  	struct xfrm_state_offload *xso = &x->xso; | ||||
|  	xfrm_address_t *saddr; | ||||
|  	xfrm_address_t *daddr; | ||||
| +	bool is_packet_offload; | ||||
|   | ||||
|  	if (!x->type_offload) | ||||
|  		return -EINVAL; | ||||
| @@ -206,9 +219,11 @@ int xfrm_dev_state_add(struct net *net, | ||||
|  	if (x->encap || x->tfcpad) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) | ||||
| +	if (xuo->flags & | ||||
| +	    ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND | XFRM_OFFLOAD_PACKET)) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| +	is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET; | ||||
|  	dev = dev_get_by_index(net, xuo->ifindex); | ||||
|  	if (!dev) { | ||||
|  		if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { | ||||
| @@ -223,7 +238,7 @@ int xfrm_dev_state_add(struct net *net, | ||||
|  					x->props.family, | ||||
|  					xfrm_smark_get(0, x)); | ||||
|  		if (IS_ERR(dst)) | ||||
| -			return 0; | ||||
| +			return (is_packet_offload) ? -EINVAL : 0; | ||||
|   | ||||
|  		dev = dst->dev; | ||||
|   | ||||
| @@ -234,7 +249,7 @@ int xfrm_dev_state_add(struct net *net, | ||||
|  	if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) { | ||||
|  		xso->dev = NULL; | ||||
|  		dev_put(dev); | ||||
| -		return 0; | ||||
| +		return (is_packet_offload) ? -EINVAL : 0; | ||||
|  	} | ||||
|   | ||||
|  	if (x->props.flags & XFRM_STATE_ESN && | ||||
| @@ -249,14 +264,28 @@ int xfrm_dev_state_add(struct net *net, | ||||
|  	/* Don't forward bit that is not implemented */ | ||||
|  	xso->flags = xuo->flags & ~XFRM_OFFLOAD_IPV6; | ||||
|   | ||||
| +	if (is_packet_offload) | ||||
| +		xso->type = XFRM_DEV_OFFLOAD_PACKET; | ||||
| +	else | ||||
| +		xso->type = XFRM_DEV_OFFLOAD_CRYPTO; | ||||
| + | ||||
|  	err = dev->xfrmdev_ops->xdo_dev_state_add(x); | ||||
|  	if (err) { | ||||
|  		xso->num_exthdrs = 0; | ||||
|  		xso->flags = 0; | ||||
|  		xso->dev = NULL; | ||||
|  		dev_put(dev); | ||||
| +		xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; | ||||
|   | ||||
| -		if (err != -EOPNOTSUPP) | ||||
| +		/* User explicitly requested packet offload mode and configured | ||||
| +		 * policy in addition to the XFRM state. So be civil to users, | ||||
| +		 * and return an error instead of taking fallback path. | ||||
| +		 * | ||||
| +		 * This WARN_ON() can be seen as a documentation for driver | ||||
| +		 * authors to do not return -EOPNOTSUPP in packet offload mode. | ||||
| +		 */ | ||||
| +		WARN_ON(err == -EOPNOTSUPP && is_packet_offload); | ||||
| +		if (err != -EOPNOTSUPP || is_packet_offload) | ||||
|  			return err; | ||||
|  	} | ||||
|   | ||||
| @@ -264,6 +293,65 @@ int xfrm_dev_state_add(struct net *net, | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(xfrm_dev_state_add); | ||||
|   | ||||
| +int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, | ||||
| +			struct xfrm_user_offload *xuo, u8 dir, | ||||
| +			struct netlink_ext_ack *extack) | ||||
| +{ | ||||
| +	struct xfrm_state_offload *xdo = &xp->xdo; | ||||
| +	struct net_device *dev; | ||||
| +	int err; | ||||
| + | ||||
| +	if (!xuo->flags || xuo->flags & ~XFRM_OFFLOAD_PACKET) { | ||||
| +		/* We support only packet offload mode and it means | ||||
| +		 * that user must set XFRM_OFFLOAD_PACKET bit. | ||||
| +		 */ | ||||
| +		NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	dev = dev_get_by_index(net, xuo->ifindex); | ||||
| +	if (!dev) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_policy_add) { | ||||
| +		xdo->dev = NULL; | ||||
| +		dev_put(dev); | ||||
| +		NL_SET_ERR_MSG(extack, "Policy offload is not supported"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	xdo->dev = dev; | ||||
| +	xdo->type = XFRM_DEV_OFFLOAD_PACKET; | ||||
| +	switch (dir) { | ||||
| +	case XFRM_POLICY_IN: | ||||
| +		xdo->dir = XFRM_DEV_OFFLOAD_IN; | ||||
| +		break; | ||||
| +	case XFRM_POLICY_OUT: | ||||
| +		xdo->dir = XFRM_DEV_OFFLOAD_OUT; | ||||
| +		break; | ||||
| +	case XFRM_POLICY_FWD: | ||||
| +		xdo->dir = XFRM_DEV_OFFLOAD_FWD; | ||||
| +		break; | ||||
| +	default: | ||||
| +		xdo->dev = NULL; | ||||
| +		dev_put(dev); | ||||
| +		NL_SET_ERR_MSG(extack, "Unrecognized oflload direction"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	err = dev->xfrmdev_ops->xdo_dev_policy_add(xp); | ||||
| +	if (err) { | ||||
| +		xdo->dev = NULL; | ||||
| +		xdo->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; | ||||
| +		xdo->dir = 0; | ||||
| +		NL_SET_ERR_MSG(extack, "Device failed to offload this policy"); | ||||
| +		return err; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(xfrm_dev_policy_add); | ||||
| + | ||||
|  bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) | ||||
|  { | ||||
|  	int mtu; | ||||
| @@ -274,8 +362,9 @@ bool xfrm_dev_offload_ok(struct sk_buff | ||||
|  	if (!x->type_offload || x->encap) | ||||
|  		return false; | ||||
|   | ||||
| -	if ((!dev || (dev == xfrm_dst_path(dst)->dev)) && | ||||
| -	    (!xdst->child->xfrm)) { | ||||
| +	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET || | ||||
| +	    ((!dev || (dev == xfrm_dst_path(dst)->dev)) && | ||||
| +	     !xdst->child->xfrm)) { | ||||
|  		mtu = xfrm_state_mtu(x, xdst->child_mtu_cached); | ||||
|  		if (skb->len <= mtu) | ||||
|  			goto ok; | ||||
| @@ -376,8 +465,10 @@ static int xfrm_dev_feat_change(struct n | ||||
|   | ||||
|  static int xfrm_dev_down(struct net_device *dev) | ||||
|  { | ||||
| -	if (dev->features & NETIF_F_HW_ESP) | ||||
| +	if (dev->features & NETIF_F_HW_ESP) { | ||||
|  		xfrm_dev_state_flush(dev_net(dev), dev, true); | ||||
| +		xfrm_dev_policy_flush(dev_net(dev), dev, true); | ||||
| +	} | ||||
|   | ||||
|  	return NOTIFY_DONE; | ||||
|  } | ||||
| --- a/net/xfrm/xfrm_output.c | ||||
| +++ b/net/xfrm/xfrm_output.c | ||||
| @@ -410,7 +410,7 @@ static int xfrm_output_one(struct sk_buf | ||||
|  	struct xfrm_state *x = dst->xfrm; | ||||
|  	struct net *net = xs_net(x); | ||||
|   | ||||
| -	if (err <= 0) | ||||
| +	if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET) | ||||
|  		goto resume; | ||||
|   | ||||
|  	do { | ||||
| @@ -568,6 +568,16 @@ int xfrm_output(struct sock *sk, struct | ||||
|  	struct xfrm_state *x = skb_dst(skb)->xfrm; | ||||
|  	int err; | ||||
|   | ||||
| +	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
| +		if (!xfrm_dev_offload_ok(skb, x)) { | ||||
| +			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); | ||||
| +			kfree_skb(skb); | ||||
| +			return -EHOSTUNREACH; | ||||
| +		} | ||||
| + | ||||
| +		return xfrm_output_resume(skb, 0); | ||||
| +	} | ||||
| + | ||||
|  	secpath_reset(skb); | ||||
|   | ||||
|  	if (xfrm_dev_offload_ok(skb, x)) { | ||||
| --- a/net/xfrm/xfrm_policy.c | ||||
| +++ b/net/xfrm/xfrm_policy.c | ||||
| @@ -423,6 +423,7 @@ void xfrm_policy_destroy(struct xfrm_pol | ||||
|  	if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer)) | ||||
|  		BUG(); | ||||
|   | ||||
| +	xfrm_dev_policy_free(policy); | ||||
|  	call_rcu(&policy->rcu, xfrm_policy_destroy_rcu); | ||||
|  } | ||||
|  EXPORT_SYMBOL(xfrm_policy_destroy); | ||||
| @@ -533,7 +534,7 @@ redo: | ||||
|  		__get_hash_thresh(net, pol->family, dir, &dbits, &sbits); | ||||
|  		h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, | ||||
|  				pol->family, nhashmask, dbits, sbits); | ||||
| -		if (!entry0) { | ||||
| +		if (!entry0 || pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
|  			hlist_del_rcu(&pol->bydst); | ||||
|  			hlist_add_head_rcu(&pol->bydst, ndsttable + h); | ||||
|  			h0 = h; | ||||
| @@ -864,7 +865,7 @@ static void xfrm_policy_inexact_list_rei | ||||
|  				break; | ||||
|  		} | ||||
|   | ||||
| -		if (newpos) | ||||
| +		if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
|  			hlist_add_behind_rcu(&policy->bydst, newpos); | ||||
|  		else | ||||
|  			hlist_add_head_rcu(&policy->bydst, &n->hhead); | ||||
| @@ -1345,7 +1346,7 @@ static void xfrm_hash_rebuild(struct wor | ||||
|  			else | ||||
|  				break; | ||||
|  		} | ||||
| -		if (newpos) | ||||
| +		if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
|  			hlist_add_behind_rcu(&policy->bydst, newpos); | ||||
|  		else | ||||
|  			hlist_add_head_rcu(&policy->bydst, chain); | ||||
| @@ -1522,7 +1523,7 @@ static void xfrm_policy_insert_inexact_l | ||||
|  			break; | ||||
|  	} | ||||
|   | ||||
| -	if (newpos) | ||||
| +	if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
|  		hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos); | ||||
|  	else | ||||
|  		hlist_add_head_rcu(&policy->bydst_inexact_list, chain); | ||||
| @@ -1559,9 +1560,12 @@ static struct xfrm_policy *xfrm_policy_i | ||||
|  			break; | ||||
|  	} | ||||
|   | ||||
| -	if (newpos) | ||||
| +	if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
|  		hlist_add_behind_rcu(&policy->bydst, &newpos->bydst); | ||||
|  	else | ||||
| +		/* Packet offload policies enter to the head | ||||
| +		 * to speed-up lookups. | ||||
| +		 */ | ||||
|  		hlist_add_head_rcu(&policy->bydst, chain); | ||||
|   | ||||
|  	return delpol; | ||||
| @@ -1767,12 +1771,41 @@ xfrm_policy_flush_secctx_check(struct ne | ||||
|  	} | ||||
|  	return err; | ||||
|  } | ||||
| + | ||||
| +static inline int xfrm_dev_policy_flush_secctx_check(struct net *net, | ||||
| +						     struct net_device *dev, | ||||
| +						     bool task_valid) | ||||
| +{ | ||||
| +	struct xfrm_policy *pol; | ||||
| +	int err = 0; | ||||
| + | ||||
| +	list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) { | ||||
| +		if (pol->walk.dead || | ||||
| +		    xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX || | ||||
| +		    pol->xdo.dev != dev) | ||||
| +			continue; | ||||
| + | ||||
| +		err = security_xfrm_policy_delete(pol->security); | ||||
| +		if (err) { | ||||
| +			xfrm_audit_policy_delete(pol, 0, task_valid); | ||||
| +			return err; | ||||
| +		} | ||||
| +	} | ||||
| +	return err; | ||||
| +} | ||||
|  #else | ||||
|  static inline int | ||||
|  xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) | ||||
|  { | ||||
|  	return 0; | ||||
|  } | ||||
| + | ||||
| +static inline int xfrm_dev_policy_flush_secctx_check(struct net *net, | ||||
| +						     struct net_device *dev, | ||||
| +						     bool task_valid) | ||||
| +{ | ||||
| +	return 0; | ||||
| +} | ||||
|  #endif | ||||
|   | ||||
|  int xfrm_policy_flush(struct net *net, u8 type, bool task_valid) | ||||
| @@ -1812,6 +1845,44 @@ out: | ||||
|  } | ||||
|  EXPORT_SYMBOL(xfrm_policy_flush); | ||||
|   | ||||
| +int xfrm_dev_policy_flush(struct net *net, struct net_device *dev, | ||||
| +			  bool task_valid) | ||||
| +{ | ||||
| +	int dir, err = 0, cnt = 0; | ||||
| +	struct xfrm_policy *pol; | ||||
| + | ||||
| +	spin_lock_bh(&net->xfrm.xfrm_policy_lock); | ||||
| + | ||||
| +	err = xfrm_dev_policy_flush_secctx_check(net, dev, task_valid); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +again: | ||||
| +	list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) { | ||||
| +		dir = xfrm_policy_id2dir(pol->index); | ||||
| +		if (pol->walk.dead || | ||||
| +		    dir >= XFRM_POLICY_MAX || | ||||
| +		    pol->xdo.dev != dev) | ||||
| +			continue; | ||||
| + | ||||
| +		__xfrm_policy_unlink(pol, dir); | ||||
| +		spin_unlock_bh(&net->xfrm.xfrm_policy_lock); | ||||
| +		cnt++; | ||||
| +		xfrm_audit_policy_delete(pol, 1, task_valid); | ||||
| +		xfrm_policy_kill(pol); | ||||
| +		spin_lock_bh(&net->xfrm.xfrm_policy_lock); | ||||
| +		goto again; | ||||
| +	} | ||||
| +	if (cnt) | ||||
| +		__xfrm_policy_inexact_flush(net); | ||||
| +	else | ||||
| +		err = -ESRCH; | ||||
| +out: | ||||
| +	spin_unlock_bh(&net->xfrm.xfrm_policy_lock); | ||||
| +	return err; | ||||
| +} | ||||
| +EXPORT_SYMBOL(xfrm_dev_policy_flush); | ||||
| + | ||||
|  int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, | ||||
|  		     int (*func)(struct xfrm_policy *, int, int, void*), | ||||
|  		     void *data) | ||||
| @@ -2113,6 +2184,9 @@ static struct xfrm_policy *xfrm_policy_l | ||||
|  			break; | ||||
|  		} | ||||
|  	} | ||||
| +	if (ret && ret->xdo.type == XFRM_DEV_OFFLOAD_PACKET) | ||||
| +		goto skip_inexact; | ||||
| + | ||||
|  	bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id); | ||||
|  	if (!bin || !xfrm_policy_find_inexact_candidates(&cand, bin, saddr, | ||||
|  							 daddr)) | ||||
| @@ -2246,6 +2320,7 @@ int xfrm_policy_delete(struct xfrm_polic | ||||
|  	pol = __xfrm_policy_unlink(pol, dir); | ||||
|  	spin_unlock_bh(&net->xfrm.xfrm_policy_lock); | ||||
|  	if (pol) { | ||||
| +		xfrm_dev_policy_delete(pol); | ||||
|  		xfrm_policy_kill(pol); | ||||
|  		return 0; | ||||
|  	} | ||||
| --- a/net/xfrm/xfrm_state.c | ||||
| +++ b/net/xfrm/xfrm_state.c | ||||
| @@ -78,6 +78,25 @@ xfrm_spi_hash(struct net *net, const xfr | ||||
|  	return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask); | ||||
|  } | ||||
|   | ||||
| +#define XFRM_STATE_INSERT(by, _n, _h, _type)                               \ | ||||
| +	{                                                                  \ | ||||
| +		struct xfrm_state *_x = NULL;                              \ | ||||
| +									   \ | ||||
| +		if (_type != XFRM_DEV_OFFLOAD_PACKET) {                    \ | ||||
| +			hlist_for_each_entry_rcu(_x, _h, by) {             \ | ||||
| +				if (_x->xso.type == XFRM_DEV_OFFLOAD_PACKET) \ | ||||
| +					continue;                          \ | ||||
| +				break;                                     \ | ||||
| +			}                                                  \ | ||||
| +		}                                                          \ | ||||
| +									   \ | ||||
| +		if (!_x || _x->xso.type == XFRM_DEV_OFFLOAD_PACKET)        \ | ||||
| +			/* SAD is empty or consist from HW SAs only */     \ | ||||
| +			hlist_add_head_rcu(_n, _h);                        \ | ||||
| +		else                                                       \ | ||||
| +			hlist_add_before_rcu(_n, &_x->by);                 \ | ||||
| +	} | ||||
| + | ||||
|  static void xfrm_hash_transfer(struct hlist_head *list, | ||||
|  			       struct hlist_head *ndsttable, | ||||
|  			       struct hlist_head *nsrctable, | ||||
| @@ -93,18 +112,19 @@ static void xfrm_hash_transfer(struct hl | ||||
|  		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr, | ||||
|  				    x->props.reqid, x->props.family, | ||||
|  				    nhashmask); | ||||
| -		hlist_add_head_rcu(&x->bydst, ndsttable + h); | ||||
| +		XFRM_STATE_INSERT(bydst, &x->bydst, ndsttable + h, x->xso.type); | ||||
|   | ||||
|  		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr, | ||||
|  				    x->props.family, | ||||
|  				    nhashmask); | ||||
| -		hlist_add_head_rcu(&x->bysrc, nsrctable + h); | ||||
| +		XFRM_STATE_INSERT(bysrc, &x->bysrc, nsrctable + h, x->xso.type); | ||||
|   | ||||
|  		if (x->id.spi) { | ||||
|  			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, | ||||
|  					    x->id.proto, x->props.family, | ||||
|  					    nhashmask); | ||||
| -			hlist_add_head_rcu(&x->byspi, nspitable + h); | ||||
| +			XFRM_STATE_INSERT(byspi, &x->byspi, nspitable + h, | ||||
| +					  x->xso.type); | ||||
|  		} | ||||
|  	} | ||||
|  } | ||||
| @@ -527,6 +547,8 @@ static enum hrtimer_restart xfrm_timer_h | ||||
|  	int err = 0; | ||||
|   | ||||
|  	spin_lock(&x->lock); | ||||
| +	xfrm_dev_state_update_curlft(x); | ||||
| + | ||||
|  	if (x->km.state == XFRM_STATE_DEAD) | ||||
|  		goto out; | ||||
|  	if (x->km.state == XFRM_STATE_EXPIRED) | ||||
| @@ -923,6 +945,49 @@ xfrm_init_tempstate(struct xfrm_state *x | ||||
|  	x->props.family = tmpl->encap_family; | ||||
|  } | ||||
|   | ||||
| +static struct xfrm_state *__xfrm_state_lookup_all(struct net *net, u32 mark, | ||||
| +						  const xfrm_address_t *daddr, | ||||
| +						  __be32 spi, u8 proto, | ||||
| +						  unsigned short family, | ||||
| +						  struct xfrm_state_offload *xdo) | ||||
| +{ | ||||
| +	unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family); | ||||
| +	struct xfrm_state *x; | ||||
| + | ||||
| +	hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) { | ||||
| +#ifdef CONFIG_XFRM_OFFLOAD | ||||
| +		if (xdo->type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
| +			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
| +				/* HW states are in the head of list, there is | ||||
| +				 * no need to iterate further. | ||||
| +				 */ | ||||
| +				break; | ||||
| + | ||||
| +			/* Packet offload: both policy and SA should | ||||
| +			 * have same device. | ||||
| +			 */ | ||||
| +			if (xdo->dev != x->xso.dev) | ||||
| +				continue; | ||||
| +		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) | ||||
| +			/* Skip HW policy for SW lookups */ | ||||
| +			continue; | ||||
| +#endif | ||||
| +		if (x->props.family != family || | ||||
| +		    x->id.spi       != spi || | ||||
| +		    x->id.proto     != proto || | ||||
| +		    !xfrm_addr_equal(&x->id.daddr, daddr, family)) | ||||
| +			continue; | ||||
| + | ||||
| +		if ((mark & x->mark.m) != x->mark.v) | ||||
| +			continue; | ||||
| +		if (!xfrm_state_hold_rcu(x)) | ||||
| +			continue; | ||||
| +		return x; | ||||
| +	} | ||||
| + | ||||
| +	return NULL; | ||||
| +} | ||||
| + | ||||
|  static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, | ||||
|  					      const xfrm_address_t *daddr, | ||||
|  					      __be32 spi, u8 proto, | ||||
| @@ -1062,6 +1127,23 @@ xfrm_state_find(const xfrm_address_t *da | ||||
|  	rcu_read_lock(); | ||||
|  	h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family); | ||||
|  	hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) { | ||||
| +#ifdef CONFIG_XFRM_OFFLOAD | ||||
| +		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
| +			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
| +				/* HW states are in the head of list, there is | ||||
| +				 * no need to iterate further. | ||||
| +				 */ | ||||
| +				break; | ||||
| + | ||||
| +			/* Packet offload: both policy and SA should | ||||
| +			 * have same device. | ||||
| +			 */ | ||||
| +			if (pol->xdo.dev != x->xso.dev) | ||||
| +				continue; | ||||
| +		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) | ||||
| +			/* Skip HW policy for SW lookups */ | ||||
| +			continue; | ||||
| +#endif | ||||
|  		if (x->props.family == encap_family && | ||||
|  		    x->props.reqid == tmpl->reqid && | ||||
|  		    (mark & x->mark.m) == x->mark.v && | ||||
| @@ -1079,6 +1161,23 @@ xfrm_state_find(const xfrm_address_t *da | ||||
|   | ||||
|  	h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family); | ||||
|  	hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h_wildcard, bydst) { | ||||
| +#ifdef CONFIG_XFRM_OFFLOAD | ||||
| +		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
| +			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) | ||||
| +				/* HW states are in the head of list, there is | ||||
| +				 * no need to iterate further. | ||||
| +				 */ | ||||
| +				break; | ||||
| + | ||||
| +			/* Packet offload: both policy and SA should | ||||
| +			 * have same device. | ||||
| +			 */ | ||||
| +			if (pol->xdo.dev != x->xso.dev) | ||||
| +				continue; | ||||
| +		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) | ||||
| +			/* Skip HW policy for SW lookups */ | ||||
| +			continue; | ||||
| +#endif | ||||
|  		if (x->props.family == encap_family && | ||||
|  		    x->props.reqid == tmpl->reqid && | ||||
|  		    (mark & x->mark.m) == x->mark.v && | ||||
| @@ -1096,8 +1195,10 @@ found: | ||||
|  	x = best; | ||||
|  	if (!x && !error && !acquire_in_progress) { | ||||
|  		if (tmpl->id.spi && | ||||
| -		    (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi, | ||||
| -					      tmpl->id.proto, encap_family)) != NULL) { | ||||
| +		    (x0 = __xfrm_state_lookup_all(net, mark, daddr, | ||||
| +						  tmpl->id.spi, tmpl->id.proto, | ||||
| +						  encap_family, | ||||
| +						  &pol->xdo)) != NULL) { | ||||
|  			to_put = x0; | ||||
|  			error = -EEXIST; | ||||
|  			goto out; | ||||
| @@ -1131,17 +1232,42 @@ found: | ||||
|  			x = NULL; | ||||
|  			goto out; | ||||
|  		} | ||||
| - | ||||
| +#ifdef CONFIG_XFRM_OFFLOAD | ||||
| +		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
| +			struct xfrm_state_offload *xdo = &pol->xdo; | ||||
| +			struct xfrm_state_offload *xso = &x->xso; | ||||
| + | ||||
| +			xso->type = XFRM_DEV_OFFLOAD_PACKET; | ||||
| +			xso->dir = xdo->dir; | ||||
| +			xso->dev = xdo->dev; | ||||
| +			error = xso->dev->xfrmdev_ops->xdo_dev_state_add(x); | ||||
| +			if (error) { | ||||
| +				xso->dir = 0; | ||||
| +				xso->dev = NULL; | ||||
| +				xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; | ||||
| +				x->km.state = XFRM_STATE_DEAD; | ||||
| +				to_put = x; | ||||
| +				x = NULL; | ||||
| +				goto out; | ||||
| +			} | ||||
| +		} | ||||
| +#endif | ||||
|  		if (km_query(x, tmpl, pol) == 0) { | ||||
|  			spin_lock_bh(&net->xfrm.xfrm_state_lock); | ||||
|  			x->km.state = XFRM_STATE_ACQ; | ||||
|  			list_add(&x->km.all, &net->xfrm.state_all); | ||||
| -			hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); | ||||
| +			XFRM_STATE_INSERT(bydst, &x->bydst, | ||||
| +					  net->xfrm.state_bydst + h, | ||||
| +					  x->xso.type); | ||||
|  			h = xfrm_src_hash(net, daddr, saddr, encap_family); | ||||
| -			hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); | ||||
| +			XFRM_STATE_INSERT(bysrc, &x->bysrc, | ||||
| +					  net->xfrm.state_bysrc + h, | ||||
| +					  x->xso.type); | ||||
|  			if (x->id.spi) { | ||||
|  				h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family); | ||||
| -				hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); | ||||
| +				XFRM_STATE_INSERT(byspi, &x->byspi, | ||||
| +						  net->xfrm.state_byspi + h, | ||||
| +						  x->xso.type); | ||||
|  			} | ||||
|  			x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; | ||||
|  			hrtimer_start(&x->mtimer, | ||||
| @@ -1151,6 +1277,16 @@ found: | ||||
|  			xfrm_hash_grow_check(net, x->bydst.next != NULL); | ||||
|  			spin_unlock_bh(&net->xfrm.xfrm_state_lock); | ||||
|  		} else { | ||||
| +#ifdef CONFIG_XFRM_OFFLOAD | ||||
| +			struct xfrm_state_offload *xso = &x->xso; | ||||
| + | ||||
| +			if (xso->type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
| +				xso->dev->xfrmdev_ops->xdo_dev_state_delete(x); | ||||
| +				xso->dir = 0; | ||||
| +				xso->dev = NULL; | ||||
| +				xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; | ||||
| +			} | ||||
| +#endif | ||||
|  			x->km.state = XFRM_STATE_DEAD; | ||||
|  			to_put = x; | ||||
|  			x = NULL; | ||||
| @@ -1246,16 +1382,19 @@ static void __xfrm_state_insert(struct x | ||||
|   | ||||
|  	h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr, | ||||
|  			  x->props.reqid, x->props.family); | ||||
| -	hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); | ||||
| +	XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h, | ||||
| +			  x->xso.type); | ||||
|   | ||||
|  	h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family); | ||||
| -	hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); | ||||
| +	XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h, | ||||
| +			  x->xso.type); | ||||
|   | ||||
|  	if (x->id.spi) { | ||||
|  		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, | ||||
|  				  x->props.family); | ||||
|   | ||||
| -		hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); | ||||
| +		XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h, | ||||
| +				  x->xso.type); | ||||
|  	} | ||||
|   | ||||
|  	hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT); | ||||
| @@ -1369,9 +1508,11 @@ static struct xfrm_state *__find_acq_cor | ||||
|  			      ktime_set(net->xfrm.sysctl_acq_expires, 0), | ||||
|  			      HRTIMER_MODE_REL_SOFT); | ||||
|  		list_add(&x->km.all, &net->xfrm.state_all); | ||||
| -		hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); | ||||
| +		XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h, | ||||
| +				  x->xso.type); | ||||
|  		h = xfrm_src_hash(net, daddr, saddr, family); | ||||
| -		hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); | ||||
| +		XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h, | ||||
| +				  x->xso.type); | ||||
|   | ||||
|  		net->xfrm.state_num++; | ||||
|   | ||||
| @@ -1742,6 +1883,8 @@ EXPORT_SYMBOL(xfrm_state_update); | ||||
|   | ||||
|  int xfrm_state_check_expire(struct xfrm_state *x) | ||||
|  { | ||||
| +	xfrm_dev_state_update_curlft(x); | ||||
| + | ||||
|  	if (!x->curlft.use_time) | ||||
|  		x->curlft.use_time = ktime_get_real_seconds(); | ||||
|   | ||||
| @@ -2043,7 +2186,8 @@ int xfrm_alloc_spi(struct xfrm_state *x, | ||||
|  		spin_lock_bh(&net->xfrm.xfrm_state_lock); | ||||
|  		x->id.spi = newspi; | ||||
|  		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family); | ||||
| -		hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); | ||||
| +		XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h, | ||||
| +				  x->xso.type); | ||||
|  		spin_unlock_bh(&net->xfrm.xfrm_state_lock); | ||||
|   | ||||
|  		err = 0; | ||||
| --- a/net/xfrm/xfrm_user.c | ||||
| +++ b/net/xfrm/xfrm_user.c | ||||
| @@ -844,6 +844,8 @@ static int copy_user_offload(struct xfrm | ||||
|  	memset(xuo, 0, sizeof(*xuo)); | ||||
|  	xuo->ifindex = xso->dev->ifindex; | ||||
|  	xuo->flags = xso->flags; | ||||
| +	if (xso->type == XFRM_DEV_OFFLOAD_PACKET) | ||||
| +		xuo->flags |= XFRM_OFFLOAD_PACKET; | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1634,6 +1636,15 @@ static struct xfrm_policy *xfrm_policy_c | ||||
|  	if (attrs[XFRMA_IF_ID]) | ||||
|  		xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); | ||||
|   | ||||
| +	/* configure the hardware if offload is requested */ | ||||
| +	if (attrs[XFRMA_OFFLOAD_DEV]) { | ||||
| +		err = xfrm_dev_policy_add(net, xp, | ||||
| +					  nla_data(attrs[XFRMA_OFFLOAD_DEV]), | ||||
| +					  p->dir, 0); | ||||
| +		if (err) | ||||
| +			goto error; | ||||
| +	} | ||||
| + | ||||
|  	return xp; | ||||
|   error: | ||||
|  	*errp = err; | ||||
| @@ -1672,6 +1683,7 @@ static int xfrm_add_policy(struct sk_buf | ||||
|  	xfrm_audit_policy_add(xp, err ? 0 : 1, true); | ||||
|   | ||||
|  	if (err) { | ||||
| +		xfrm_dev_policy_delete(xp); | ||||
|  		security_xfrm_policy_free(xp->security); | ||||
|  		kfree(xp); | ||||
|  		return err; | ||||
| @@ -1783,6 +1795,8 @@ static int dump_one_policy(struct xfrm_p | ||||
|  		err = xfrm_mark_put(skb, &xp->mark); | ||||
|  	if (!err) | ||||
|  		err = xfrm_if_id_put(skb, xp->if_id); | ||||
| +	if (!err && xp->xdo.dev) | ||||
| +		err = copy_user_offload(&xp->xdo, skb); | ||||
|  	if (err) { | ||||
|  		nlmsg_cancel(skb, nlh); | ||||
|  		return err; | ||||
| @@ -2958,6 +2972,8 @@ static int build_acquire(struct sk_buff | ||||
|  		err = xfrm_mark_put(skb, &xp->mark); | ||||
|  	if (!err) | ||||
|  		err = xfrm_if_id_put(skb, xp->if_id); | ||||
| +	if (!err && xp->xdo.dev) | ||||
| +		err = copy_user_offload(&xp->xdo, skb); | ||||
|  	if (err) { | ||||
|  		nlmsg_cancel(skb, nlh); | ||||
|  		return err; | ||||
| @@ -3076,6 +3092,8 @@ static int build_polexpire(struct sk_buf | ||||
|  		err = xfrm_mark_put(skb, &xp->mark); | ||||
|  	if (!err) | ||||
|  		err = xfrm_if_id_put(skb, xp->if_id); | ||||
| +	if (!err && xp->xdo.dev) | ||||
| +		err = copy_user_offload(&xp->xdo, skb); | ||||
|  	if (err) { | ||||
|  		nlmsg_cancel(skb, nlh); | ||||
|  		return err; | ||||
| @@ -3159,6 +3177,8 @@ static int xfrm_notify_policy(struct xfr | ||||
|  		err = xfrm_mark_put(skb, &xp->mark); | ||||
|  	if (!err) | ||||
|  		err = xfrm_if_id_put(skb, xp->if_id); | ||||
| +	if (!err && xp->xdo.dev) | ||||
| +		err = copy_user_offload(&xp->xdo, skb); | ||||
|  	if (err) | ||||
|  		goto out_free_skb; | ||||
|   | ||||
| @@ -0,0 +1,37 @@ | ||||
| diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h | ||||
| index 4f1c0f8..381f28e 100644 | ||||
| --- a/include/linux/bitfield.h | ||||
| +++ b/include/linux/bitfield.h | ||||
| @@ -99,6 +99,32 @@ | ||||
|  		((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);	\ | ||||
|  	}) | ||||
|   | ||||
| +#define __BF_CHECK_POW2(n)	BUILD_BUG_ON_ZERO(((n) & ((n) - 1)) != 0) | ||||
| + | ||||
| +/** | ||||
| + * FIELD_PREP_CONST() - prepare a constant bitfield element | ||||
| + * @_mask: shifted mask defining the field's length and position | ||||
| + * @_val:  value to put in the field | ||||
| + * | ||||
| + * FIELD_PREP_CONST() masks and shifts up the value.  The result should | ||||
| + * be combined with other fields of the bitfield using logical OR. | ||||
| + * | ||||
| + * Unlike FIELD_PREP() this is a constant expression and can therefore | ||||
| + * be used in initializers. Error checking is less comfortable for this | ||||
| + * version, and non-constant masks cannot be used. | ||||
| + */ | ||||
| +#define FIELD_PREP_CONST(_mask, _val)					\ | ||||
| +	(								\ | ||||
| +		/* mask must be non-zero */				\ | ||||
| +		BUILD_BUG_ON_ZERO((_mask) == 0) +			\ | ||||
| +		/* check if value fits */				\ | ||||
| +		BUILD_BUG_ON_ZERO(~((_mask) >> __bf_shf(_mask)) & (_val)) + \ | ||||
| +		/* check if mask is contiguous */			\ | ||||
| +		__BF_CHECK_POW2((_mask) + (1ULL << __bf_shf(_mask))) +	\ | ||||
| +		/* and create the value */				\ | ||||
| +		(((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask))	\ | ||||
| +	) | ||||
| + | ||||
|  /** | ||||
|   * FIELD_GET() - extract a bitfield element | ||||
|   * @_mask: shifted mask defining the field's length and position | ||||
| @@ -0,0 +1,23 @@ | ||||
| diff --git a/include/linux/kernel.h b/include/linux/kernel.h | ||||
| index 1fdb251..24a0b27 100644 | ||||
| --- a/include/linux/kernel.h | ||||
| +++ b/include/linux/kernel.h | ||||
| @@ -197,6 +197,18 @@ | ||||
|   */ | ||||
|  #define lower_32_bits(n) ((u32)(n)) | ||||
|   | ||||
| +/** | ||||
| + * upper_16_bits - return bits 16-31 of a number | ||||
| + * @n: the number we're accessing | ||||
| + */ | ||||
| +#define upper_16_bits(n) ((u16)((n) >> 16)) | ||||
| + | ||||
| +/** | ||||
| + * lower_16_bits - return bits 0-15 of a number | ||||
| + * @n: the number we're accessing | ||||
| + */ | ||||
| +#define lower_16_bits(n) ((u16)((n) & 0xffff)) | ||||
| + | ||||
|  struct completion; | ||||
|  struct pt_regs; | ||||
|  struct user; | ||||
| @@ -442,7 +442,7 @@ Index: linux-5.4.260/drivers/mtd/nand/spi/macronix.c | ||||
|  		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), | ||||
|  		     NAND_ECCREQ(4, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -116,51 +118,194 @@ static const struct spinand_info macroni | ||||
| @@ -116,23 +118,76 @@ static const struct spinand_info macroni | ||||
|  					      &update_cache_variants), | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), | ||||
| @@ -455,16 +455,18 @@ Index: linux-5.4.260/drivers/mtd/nand/spi/macronix.c | ||||
|  					      &write_cache_variants, | ||||
|  					      &update_cache_variants), | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
| -		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), | ||||
| -	SPINAND_INFO("MX35LF4GE4AD", 0x37, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
| +	SPINAND_INFO("MX35LF4GE4AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37), | ||||
| +		     NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
|  		     NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
|  					      &write_cache_variants, | ||||
|  					      &update_cache_variants), | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
| +	SPINAND_INFO("MX35LF1G24AD", | ||||
| @@ -476,30 +478,16 @@ Index: linux-5.4.260/drivers/mtd/nand/spi/macronix.c | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), | ||||
| -	SPINAND_INFO("MX35LF4GE4AD", 0x37, | ||||
| -		     NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), | ||||
| -	SPINAND_INFO("MX35LF2G14AC", 0x20, | ||||
| +	SPINAND_INFO("MX35LF2G24AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), | ||||
| +		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
|  					      &write_cache_variants, | ||||
|  					      &update_cache_variants), | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), | ||||
| -}; | ||||
| - | ||||
| -static int macronix_spinand_detect(struct spinand_device *spinand) | ||||
| -{ | ||||
| -	u8 *id = spinand->id.data; | ||||
| -	int ret; | ||||
| - | ||||
| -	/* | ||||
| -	 * Macronix SPI NAND read ID needs a dummy byte, so the first byte in | ||||
| -	 * raw_id is garbage. | ||||
| -	 */ | ||||
| -	if (id[1] != SPINAND_MFR_MACRONIX) | ||||
| -		return 0; | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), | ||||
| +	SPINAND_INFO("MX35LF4G24AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), | ||||
| +		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), | ||||
| @@ -529,131 +517,133 @@ Index: linux-5.4.260/drivers/mtd/nand/spi/macronix.c | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|   | ||||
| -	ret = spinand_match_and_init(spinand, macronix_spinand_table, | ||||
| -				     ARRAY_SIZE(macronix_spinand_table), | ||||
| -				     id[2]); | ||||
| -	if (ret) | ||||
| -		return ret; | ||||
| + | ||||
| +	SPINAND_INFO("MX35LF2G14AC", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20), | ||||
| +		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), | ||||
| +		     NAND_ECCREQ(4, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), | ||||
|  		     NAND_ECCREQ(4, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -141,7 +196,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF4G24AD", 0xb5, | ||||
| +	SPINAND_INFO("MX35UF4G24AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5), | ||||
| +		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -150,7 +206,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF4GE4AD", 0xb7, | ||||
| +	SPINAND_INFO("MX35UF4GE4AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7), | ||||
| +		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -159,7 +216,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF2G14AC", 0xa0, | ||||
| +	SPINAND_INFO("MX35UF2G14AC", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0), | ||||
| +		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), | ||||
| +		     NAND_ECCREQ(4, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), | ||||
|  		     NAND_ECCREQ(4, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -168,7 +226,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF2G24AD", 0xa4, | ||||
| +	SPINAND_INFO("MX35UF2G24AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4), | ||||
| +		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -177,7 +236,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF2GE4AD", 0xa6, | ||||
| +	SPINAND_INFO("MX35UF2GE4AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6), | ||||
| +		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -186,7 +246,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF2GE4AC", 0xa2, | ||||
| +	SPINAND_INFO("MX35UF2GE4AC", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2), | ||||
| +		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(4, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(4, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -195,7 +256,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF1G14AC", 0x90, | ||||
| +	SPINAND_INFO("MX35UF1G14AC", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90), | ||||
| +		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(4, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(4, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -204,7 +266,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF1G24AD", 0x94, | ||||
| +	SPINAND_INFO("MX35UF1G24AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94), | ||||
| +		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -213,7 +276,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF1GE4AD", 0x96, | ||||
| +	SPINAND_INFO("MX35UF1GE4AD", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96), | ||||
| +		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(8, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(8, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -222,7 +286,8 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| -	SPINAND_INFO("MX35UF1GE4AC", 0x92, | ||||
| +	SPINAND_INFO("MX35UF1GE4AC", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92), | ||||
| +		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(4, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
| +				     mx35lf1ge4ab_ecc_get_status)), | ||||
|  		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), | ||||
|  		     NAND_ECCREQ(4, 512), | ||||
|  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| @@ -231,6 +296,7 @@ static const struct spinand_info macroni | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, | ||||
|  				     mx35lf1ge4ab_ecc_get_status)), | ||||
| + | ||||
|  }; | ||||
|   | ||||
| -	return 1; | ||||
| -} | ||||
| +}; | ||||
|  static int macronix_spinand_detect(struct spinand_device *spinand) | ||||
| --- a/drivers/mtd/nand/spi/macronix.c | ||||
| +++ b/drivers/mtd/nand/spi/macronix.c | ||||
| @@ -255,7 +255,6 @@ static int macronix_spinand_detect(struc | ||||
|  } | ||||
|   | ||||
|  static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { | ||||
| -	.detect = macronix_spinand_detect, | ||||
|  }; | ||||
|  const struct spinand_manufacturer macronix_spinand_manufacturer = { | ||||
|   | ||||
| --- a/drivers/mtd/nand/spi/macronix.c | ||||
| +++ b/drivers/mtd/nand/spi/macronix.c | ||||
| @@ -326,5 +326,7 @@ static const struct spinand_manufacturer | ||||
|  const struct spinand_manufacturer macronix_spinand_manufacturer = { | ||||
|  	.id = SPINAND_MFR_MACRONIX, | ||||
|  	.name = "Macronix", | ||||
| @@ -661,6 +651,7 @@ Index: linux-5.4.260/drivers/mtd/nand/spi/macronix.c | ||||
| +	.nchips = ARRAY_SIZE(macronix_spinand_table), | ||||
|  	.ops = ¯onix_spinand_manuf_ops, | ||||
|  }; | ||||
|   | ||||
| Index: linux-5.4.260/drivers/mtd/nand/spi/micron.c | ||||
| =================================================================== | ||||
| --- linux-5.4.260.orig/drivers/mtd/nand/spi/micron.c | ||||
|   | ||||
| @@ -1,22 +1,21 @@ | ||||
| From 1631a36b9ac022ce6ffb58b039a7e85ad3414ed5 Mon Sep 17 00:00:00 2001 | ||||
| From: Sam Shih <sam.shih@mediatek.com> | ||||
| Date: Fri, 2 Jun 2023 13:06:01 +0800 | ||||
| Subject: [PATCH]  | ||||
|  [backport-networking-drivers][999-1709-net-phy-sfp-add-rollball-support.patch] | ||||
| From 785d2cda90b7d5d3935fd1ce06f479181ba1d9a4 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Tue, 19 Nov 2024 13:23:07 +0800 | ||||
| Subject: [PATCH] net: phy: sfp: add rollball support | ||||
| 
 | ||||
| ---
 | ||||
|  drivers/net/phy/marvell.c     |   2 +- | ||||
|  drivers/net/phy/marvell10g.c  | 168 +++++++++++++-- | ||||
|  drivers/net/phy/mdio-i2c.c    | 309 +++++++++++++++++++++++++++- | ||||
|  drivers/net/phy/phylink.c     |  74 +++++-- | ||||
|  drivers/net/phy/sfp-bus.c     | 102 +--------- | ||||
|  drivers/net/phy/sfp.c         | 373 +++++++++++++++++++++++++++++----- | ||||
|  drivers/net/phy/marvell10g.c  | 168 ++++++++++++- | ||||
|  drivers/net/phy/mdio-i2c.c    | 309 +++++++++++++++++++++++- | ||||
|  drivers/net/phy/phylink.c     |  76 ++++-- | ||||
|  drivers/net/phy/sfp-bus.c     | 105 +------- | ||||
|  drivers/net/phy/sfp.c         | 442 ++++++++++++++++++++++++++++++---- | ||||
|  drivers/net/phy/sfp.h         |  11 +- | ||||
|  include/linux/mdio/mdio-i2c.h |  10 +- | ||||
|  8 files changed, 874 insertions(+), 175 deletions(-) | ||||
|  8 files changed, 943 insertions(+), 180 deletions(-) | ||||
| 
 | ||||
| diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
 | ||||
| index 49801c2eb..f25881745 100644
 | ||||
| index 49801c2..f258817 100644
 | ||||
| --- a/drivers/net/phy/marvell.c
 | ||||
| +++ b/drivers/net/phy/marvell.c
 | ||||
| @@ -2175,7 +2175,7 @@ static struct phy_driver marvell_drivers[] = {
 | ||||
| @@ -29,7 +28,7 @@ index 49801c2eb..f25881745 100644 | ||||
|  		.config_init = &m88e1111_config_init, | ||||
|  		.config_aneg = &marvell_config_aneg, | ||||
| diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
 | ||||
| index 1e4631761..7d080d52e 100644
 | ||||
| index 1e46317..7d080d5 100644
 | ||||
| --- a/drivers/net/phy/marvell10g.c
 | ||||
| +++ b/drivers/net/phy/marvell10g.c
 | ||||
| @@ -32,6 +32,15 @@
 | ||||
| @@ -302,7 +301,7 @@ index 1e4631761..7d080d52e 100644 | ||||
|  }; | ||||
|   | ||||
| diff --git a/drivers/net/phy/mdio-i2c.c b/drivers/net/phy/mdio-i2c.c
 | ||||
| index 09200a70b..85db63c33 100644
 | ||||
| index 09200a7..85db63c 100644
 | ||||
| --- a/drivers/net/phy/mdio-i2c.c
 | ||||
| +++ b/drivers/net/phy/mdio-i2c.c
 | ||||
| @@ -12,6 +12,7 @@
 | ||||
| @@ -654,7 +653,7 @@ index 09200a70b..85db63c33 100644 | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(mdio_i2c_alloc); | ||||
| diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
 | ||||
| index f360d9225..67f34ed4c 100644
 | ||||
| index f360d92..7412a7b 100644
 | ||||
| --- a/drivers/net/phy/phylink.c
 | ||||
| +++ b/drivers/net/phy/phylink.c
 | ||||
| @@ -483,62 +483,105 @@ static void phylink_resolve(struct work_struct *w)
 | ||||
| @@ -777,6 +776,15 @@ index f360d9225..67f34ed4c 100644 | ||||
|  		pl->mac_link_dropped = false; | ||||
|  		queue_work(system_power_efficient_wq, &pl->resolve); | ||||
|  	} | ||||
| @@ -785,7 +828,7 @@ static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
 | ||||
|  { | ||||
|  	if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED || | ||||
|  		    (pl->cfg_link_an_mode == MLO_AN_INBAND && | ||||
| -		     phy_interface_mode_is_8023z(interface))))
 | ||||
| +		     phy_interface_mode_is_8023z(interface) && !pl->sfp_bus)))
 | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	if (pl->phydev) | ||||
| @@ -1014,7 +1057,8 @@ void phylink_start(struct phylink *pl)
 | ||||
|  		if (irq <= 0) | ||||
|  			mod_timer(&pl->link_poll, jiffies + HZ); | ||||
| @@ -788,7 +796,7 @@ index f360d9225..67f34ed4c 100644 | ||||
|  	if (pl->phydev) | ||||
|  		phy_start(pl->phydev); | ||||
| diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
 | ||||
| index a2f451c31..4be24406b 100644
 | ||||
| index a2f451c..7d16e72 100644
 | ||||
| --- a/drivers/net/phy/sfp-bus.c
 | ||||
| +++ b/drivers/net/phy/sfp-bus.c
 | ||||
| @@ -10,12 +10,6 @@
 | ||||
| @@ -919,7 +927,17 @@ index a2f451c31..4be24406b 100644 | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(sfp_parse_support); | ||||
|   | ||||
| @@ -737,12 +650,13 @@ void sfp_link_down(struct sfp_bus *bus)
 | ||||
| @@ -392,7 +305,8 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus,
 | ||||
|  	if (phylink_test(link_modes, 5000baseT_Full)) | ||||
|  		return PHY_INTERFACE_MODE_5GBASER; | ||||
|   | ||||
| -	if (phylink_test(link_modes, 2500baseX_Full))
 | ||||
| +	if (phylink_test(link_modes, 2500baseX_Full) ||
 | ||||
| +	    phylink_test(link_modes, 2500baseT_Full))
 | ||||
|  		return PHY_INTERFACE_MODE_2500BASEX; | ||||
|   | ||||
|  	if (phylink_test(link_modes, 1000baseT_Half) || | ||||
| @@ -737,12 +651,13 @@ void sfp_link_down(struct sfp_bus *bus)
 | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(sfp_link_down); | ||||
|   | ||||
| @@ -936,18 +954,19 @@ index a2f451c31..4be24406b 100644 | ||||
|  	if (ops && ops->module_insert) | ||||
|  		ret = ops->module_insert(bus->upstream, id); | ||||
| diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
 | ||||
| index f8d1742e0..0fdf5d6d4 100644
 | ||||
| index a8eeb57..3ecc6aa 100644
 | ||||
| --- a/drivers/net/phy/sfp.c
 | ||||
| +++ b/drivers/net/phy/sfp.c
 | ||||
| @@ -165,6 +165,7 @@ static const enum gpiod_flags gpio_flags[] = {
 | ||||
|   * on board (for a copper SFP) time to initialise. | ||||
| @@ -186,7 +186,7 @@ static const enum gpiod_flags gpio_flags[] = {
 | ||||
|   * R_PHY_RETRY is the number of attempts. | ||||
|   */ | ||||
|  #define T_WAIT			msecs_to_jiffies(50) | ||||
| +#define T_WAIT_ROLLBALL		msecs_to_jiffies(25000)
 | ||||
|  #define T_START_UP		msecs_to_jiffies(300) | ||||
|  #define T_START_UP_BAD_GPON	msecs_to_jiffies(60000) | ||||
|  #define T_PHY_RETRY		msecs_to_jiffies(50) | ||||
| -#define R_PHY_RETRY		12
 | ||||
| +#define R_PHY_RETRY		25
 | ||||
|   | ||||
| @@ -204,8 +205,11 @@ static const enum gpiod_flags gpio_flags[] = {
 | ||||
|  /* SFP module presence detection is poor: the three MOD DEF signals are | ||||
|   * the same length on the PCB, which means it's possible for MOD DEF 0 to | ||||
| @@ -204,8 +204,11 @@ static const enum gpiod_flags gpio_flags[] = {
 | ||||
|   | ||||
|  /* SFP modules appear to always have their PHY configured for bus address | ||||
|   * 0x56 (which with mdio-i2c, translates to a PHY address of 22). | ||||
| @@ -960,7 +979,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|   | ||||
|  struct sff_data { | ||||
|  	unsigned int gpios; | ||||
| @@ -217,6 +221,7 @@ struct sfp {
 | ||||
| @@ -217,6 +220,7 @@ struct sfp {
 | ||||
|  	struct i2c_adapter *i2c; | ||||
|  	struct mii_bus *i2c_mii; | ||||
|  	struct sfp_bus *sfp_bus; | ||||
| @@ -968,20 +987,22 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  	struct phy_device *mod_phy; | ||||
|  	const struct sff_data *type; | ||||
|  	size_t i2c_block_size; | ||||
| @@ -233,6 +238,7 @@ struct sfp {
 | ||||
| @@ -233,7 +237,9 @@ struct sfp {
 | ||||
|  	bool need_poll; | ||||
|   | ||||
|  	struct mutex st_mutex;			/* Protects state */ | ||||
| +	unsigned int state_hw_mask;
 | ||||
|  	unsigned int state_soft_mask; | ||||
| +	unsigned int state_ignore_mask;
 | ||||
|  	unsigned int state; | ||||
|  	struct delayed_work poll; | ||||
|  	struct delayed_work timeout; | ||||
| @@ -249,6 +255,10 @@ struct sfp {
 | ||||
|  	struct sfp_eeprom_id id; | ||||
|  	unsigned int module_power_mW; | ||||
|  	unsigned int module_t_start_up; | ||||
| +	unsigned int module_t_wait;
 | ||||
| +	bool tx_fault_ignore;
 | ||||
| +	unsigned int phy_t_retry;
 | ||||
| +
 | ||||
| +	const struct sfp_quirk *quirk;
 | ||||
|   | ||||
| @@ -1006,7 +1027,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  	return false; | ||||
|  } | ||||
|   | ||||
| @@ -303,6 +325,180 @@ static const struct of_device_id sfp_of_match[] = {
 | ||||
| @@ -303,6 +325,235 @@ static const struct of_device_id sfp_of_match[] = {
 | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, sfp_of_match); | ||||
|   | ||||
| @@ -1015,9 +1036,18 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
| +	sfp->module_t_start_up = T_START_UP_BAD_GPON;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_ignore_los(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	/* This forces LOS to zero, so we ignore transitions */
 | ||||
| +	sfp->state_ignore_mask |= SFP_F_LOS;
 | ||||
| +	/* Make sure that LOS options are clear */
 | ||||
| +	sfp->id.ext.options &= ~cpu_to_be16(SFP_OPTIONS_LOS_INVERTED |
 | ||||
| +					    SFP_OPTIONS_LOS_NORMAL);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	sfp->tx_fault_ignore = true;
 | ||||
| +	sfp->state_ignore_mask |= SFP_F_TX_FAULT;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_ruijie_gbic(struct sfp *sfp)
 | ||||
| @@ -1025,6 +1055,43 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
| +	sfp->mdio_protocol = MDIO_I2C_NONE;
 | ||||
| +}
 | ||||
| +
 | ||||
| +// For 10GBASE-T short-reach modules
 | ||||
| +static void sfp_fixup_10gbaset_30m(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	sfp->id.base.connector = SFF8024_CONNECTOR_RJ45;
 | ||||
| +	sfp->id.base.extended_cc = SFF8024_ECC_10GBASE_T_SR;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_rollball(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	sfp->mdio_protocol = MDIO_I2C_ROLLBALL;
 | ||||
| +
 | ||||
| +	/* RollBall modules may disallow access to PHY registers for up to 25
 | ||||
| +	 * seconds, and the reads return 0xffff before that. Increase the time
 | ||||
| +	 * between PHY probe retries from 50ms to 1s so that we will wait for
 | ||||
| +	 * the PHY for a sufficient amount of time.
 | ||||
| +	 */
 | ||||
| +	sfp->phy_t_retry = msecs_to_jiffies(1000);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_fs_2_5gt(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	sfp_fixup_rollball(sfp);
 | ||||
| +	sfp_fixup_ignore_los(sfp);
 | ||||
| +
 | ||||
| +	/* The RollBall fixup is not enough for FS modules, the PHY chip inside
 | ||||
| +	 * them does not return 0xffff for PHY ID registers in all MMDs for the
 | ||||
| +	 * while initializing. They need a 4 second wait before accessing PHY.
 | ||||
| +	 */
 | ||||
| +	sfp->module_t_wait = msecs_to_jiffies(4000);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_fs_10gt(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	sfp_fixup_10gbaset_30m(sfp);
 | ||||
| +	sfp_fixup_fs_2_5gt(sfp);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_halny_gsfp(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	/* Ignore the TX_FAULT and LOS signals on this module.
 | ||||
| @@ -1034,12 +1101,6 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
| +	sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_rollball(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	sfp->mdio_protocol = MDIO_I2C_ROLLBALL;
 | ||||
| +	sfp->module_t_wait = T_WAIT_ROLLBALL;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sfp_fixup_rollball_cc(struct sfp *sfp)
 | ||||
| +{
 | ||||
| +	sfp_fixup_rollball(sfp);
 | ||||
| @@ -1107,6 +1168,21 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
| +	SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", '\0', sfp_quirk_2500basex,
 | ||||
| +		  sfp_fixup_long_startup),
 | ||||
| +
 | ||||
| +	// Fiberstore SFP-10G-T doesn't identify as copper, uses the Rollball
 | ||||
| +	// protocol to talk to the PHY and needs 4 sec wait before probing the
 | ||||
| +	// PHY.
 | ||||
| +	SFP_QUIRK_F("FS", "SFP-10G-T", '\0', sfp_fixup_fs_10gt),
 | ||||
| +
 | ||||
| +	// Fiberstore SFP-2.5G-T uses Rollball protocol to talk to the PHY and
 | ||||
| +	// needs 4 sec wait before probing the PHY.
 | ||||
| +	SFP_QUIRK_F("FS", "SFP-2.5G-T", '\0', sfp_fixup_fs_2_5gt),
 | ||||
| +	SFP_QUIRK_F("FS", "SFP-2.5G-T-I", '\0', sfp_fixup_fs_2_5gt),
 | ||||
| +
 | ||||
| +	// Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd
 | ||||
| +	// NRZ in their EEPROM
 | ||||
| +	SFP_QUIRK("FS", "GPON-ONU-34-20BI", '\0', sfp_quirk_2500basex,
 | ||||
| +		  sfp_fixup_ignore_tx_fault),
 | ||||
| +
 | ||||
| +	SFP_QUIRK_F("HALNy", "HL-GSFP", '\0', sfp_fixup_halny_gsfp),
 | ||||
| +
 | ||||
| +	// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd NRZ in
 | ||||
| @@ -1187,7 +1263,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  static unsigned long poll_jiffies; | ||||
|   | ||||
|  static unsigned int sfp_gpio_get_state(struct sfp *sfp) | ||||
| @@ -414,9 +610,6 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
 | ||||
| @@ -414,9 +664,6 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
 | ||||
|   | ||||
|  static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) | ||||
|  { | ||||
| @@ -1197,7 +1273,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  	if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| @@ -424,7 +617,15 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
 | ||||
| @@ -424,7 +671,15 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
 | ||||
|  	sfp->read = sfp_i2c_read; | ||||
|  	sfp->write = sfp_i2c_write; | ||||
|   | ||||
| @@ -1214,7 +1290,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  	if (IS_ERR(i2c_mii)) | ||||
|  		return PTR_ERR(i2c_mii); | ||||
|   | ||||
| @@ -442,6 +643,12 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
 | ||||
| @@ -442,6 +697,12 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -1227,7 +1303,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  /* Interface */ | ||||
|  static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) | ||||
|  { | ||||
| @@ -487,17 +694,18 @@ static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
 | ||||
| @@ -487,17 +748,19 @@ static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
 | ||||
|  static void sfp_soft_start_poll(struct sfp *sfp) | ||||
|  { | ||||
|  	const struct sfp_eeprom_id *id = &sfp->id; | ||||
| @@ -1251,11 +1327,12 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
| +		mask |= SFP_F_LOS;
 | ||||
| +
 | ||||
| +	// Poll the soft state for hardware pins we want to ignore
 | ||||
| +	sfp->state_soft_mask = ~sfp->state_hw_mask & mask;
 | ||||
| +	sfp->state_soft_mask = ~sfp->state_hw_mask & ~sfp->state_ignore_mask &
 | ||||
| +			       mask;
 | ||||
|   | ||||
|  	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && | ||||
|  	    !sfp->need_poll) | ||||
| @@ -511,10 +719,11 @@ static void sfp_soft_stop_poll(struct sfp *sfp)
 | ||||
| @@ -511,10 +774,11 @@ static void sfp_soft_stop_poll(struct sfp *sfp)
 | ||||
|   | ||||
|  static unsigned int sfp_get_state(struct sfp *sfp) | ||||
|  { | ||||
| @@ -1270,7 +1347,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  		state |= sfp_soft_get_state(sfp); | ||||
|   | ||||
|  	return state; | ||||
| @@ -1448,12 +1657,12 @@ static void sfp_sm_phy_detach(struct sfp *sfp)
 | ||||
| @@ -1448,12 +1712,12 @@ static void sfp_sm_phy_detach(struct sfp *sfp)
 | ||||
|  	sfp->mod_phy = NULL; | ||||
|  } | ||||
|   | ||||
| @@ -1285,7 +1362,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  	if (phy == ERR_PTR(-ENODEV)) | ||||
|  		return PTR_ERR(phy); | ||||
|  	if (IS_ERR(phy)) { | ||||
| @@ -1548,6 +1757,14 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
 | ||||
| @@ -1548,6 +1812,14 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -1300,7 +1377,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  /* Probe a SFP for a PHY device if the module supports copper - the PHY | ||||
|   * normally sits at I2C bus address 0x56, and may either be a clause 22 | ||||
|   * or clause 45 PHY. | ||||
| @@ -1563,36 +1780,52 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp)
 | ||||
| @@ -1563,36 +1835,52 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp)
 | ||||
|  { | ||||
|  	int err = 0; | ||||
|   | ||||
| @@ -1367,7 +1444,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  			/* The module appears not to implement bus address | ||||
|  			 * 0xa2, so assume that the module powers up in the | ||||
|  			 * indicated mode. | ||||
| @@ -1609,13 +1842,21 @@ static int sfp_module_parse_power(struct sfp *sfp)
 | ||||
| @@ -1609,13 +1897,21 @@ static int sfp_module_parse_power(struct sfp *sfp)
 | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| @@ -1391,7 +1468,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  			 power_mW / 1000, (power_mW / 100) % 10); | ||||
|  		return 0; | ||||
|  	} | ||||
| @@ -1692,7 +1933,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 | ||||
| @@ -1692,7 +1988,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 | ||||
|  { | ||||
|  	/* SFP module inserted - read I2C data */ | ||||
|  	struct sfp_eeprom_id id; | ||||
| @@ -1400,7 +1477,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  	u8 check; | ||||
|  	int ret; | ||||
|   | ||||
| @@ -1747,10 +1988,16 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 | ||||
| @@ -1747,10 +2043,16 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 | ||||
|  	 */ | ||||
|  	cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS       ", 16); | ||||
|   | ||||
| @@ -1418,7 +1495,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  			dev_warn(sfp->dev, | ||||
|  				 "EEPROM base structure checksum failure (0x%02x != 0x%02x)\n", | ||||
|  				 check, id.base.cc_base); | ||||
| @@ -1819,11 +2066,33 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 | ||||
| @@ -1819,11 +2121,36 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
|   | ||||
| @@ -1436,8 +1513,9 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
| +
 | ||||
| +	sfp->module_t_start_up = T_START_UP;
 | ||||
| +	sfp->module_t_wait = T_WAIT;
 | ||||
| +	sfp->phy_t_retry = T_PHY_RETRY;
 | ||||
| +
 | ||||
| +	sfp->tx_fault_ignore = false;
 | ||||
| +	sfp->state_ignore_mask = 0;
 | ||||
| +
 | ||||
| +	if (sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SFI ||
 | ||||
| +	    sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SR ||
 | ||||
| @@ -1453,10 +1531,12 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
| +	sfp->quirk = sfp_lookup_quirk(&id);
 | ||||
| +	if (sfp->quirk && sfp->quirk->fixup)
 | ||||
| +		sfp->quirk->fixup(sfp);
 | ||||
| +
 | ||||
| +	sfp->state_hw_mask &= ~sfp->state_ignore_mask;
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1936,7 +2205,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | ||||
| @@ -1936,7 +2263,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | ||||
|  			break; | ||||
|   | ||||
|  		/* Report the module insertion to the upstream device */ | ||||
| @@ -1466,7 +1546,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  		if (err < 0) { | ||||
|  			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||
|  			break; | ||||
| @@ -1995,6 +2265,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
| @@ -1995,6 +2323,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
|  			sfp_module_stop(sfp->sfp_bus); | ||||
|  		if (sfp->mod_phy) | ||||
|  			sfp_sm_phy_detach(sfp); | ||||
| @@ -1475,7 +1555,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  		sfp_module_tx_disable(sfp); | ||||
|  		sfp_soft_stop_poll(sfp); | ||||
|  		sfp_sm_next(sfp, SFP_S_DOWN, 0); | ||||
| @@ -2018,9 +2290,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
| @@ -2018,9 +2348,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
|   | ||||
|  		/* We need to check the TX_FAULT state, which is not defined | ||||
|  		 * while TX_DISABLE is asserted. The earliest we want to do | ||||
| @@ -1488,7 +1568,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  		break; | ||||
|   | ||||
|  	case SFP_S_WAIT: | ||||
| @@ -2034,8 +2307,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
| @@ -2034,8 +2365,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
|  			 * deasserting. | ||||
|  			 */ | ||||
|  			timeout = sfp->module_t_start_up; | ||||
| @@ -1499,7 +1579,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  			else | ||||
|  				timeout = 1; | ||||
|   | ||||
| @@ -2057,6 +2330,12 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
| @@ -2057,6 +2388,12 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
|  				     sfp->sm_fault_retries == N_FAULT_INIT); | ||||
|  		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { | ||||
|  	init_done: | ||||
| @@ -1512,7 +1592,23 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  			sfp->sm_phy_retries = R_PHY_RETRY; | ||||
|  			goto phy_probe; | ||||
|  		} | ||||
| @@ -2409,6 +2688,8 @@ static int sfp_probe(struct platform_device *pdev)
 | ||||
| @@ -2070,9 +2407,13 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | ||||
|  		 * clear.  Probe for the PHY and check the LOS state. | ||||
|  		 */ | ||||
|  		ret = sfp_sm_probe_for_phy(sfp); | ||||
| -		if (ret == -ENODEV) {
 | ||||
| +		if (ret == -ENODEV || ret == -EINVAL) {
 | ||||
|  			if (--sfp->sm_phy_retries) { | ||||
| -				sfp_sm_next(sfp, SFP_S_INIT_PHY, T_PHY_RETRY);
 | ||||
| +				sfp_sm_next(sfp, SFP_S_INIT_PHY,
 | ||||
| +					    sfp->phy_t_retry);
 | ||||
| +				dev_info(sfp->dev,
 | ||||
| +					"no PHY detected, %u tries left\n",
 | ||||
| +					sfp->sm_phy_retries);
 | ||||
|  				break; | ||||
|  			} else { | ||||
|  				dev_info(sfp->dev, "no PHY detected\n"); | ||||
| @@ -2409,6 +2750,8 @@ static int sfp_probe(struct platform_device *pdev)
 | ||||
|  				return PTR_ERR(sfp->gpio[i]); | ||||
|  		} | ||||
|   | ||||
| @@ -1522,7 +1618,7 @@ index f8d1742e0..0fdf5d6d4 100644 | ||||
|  	sfp->set_state = sfp_gpio_set_state; | ||||
|   | ||||
| diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h
 | ||||
| index b83f70526..f533e2dd6 100644
 | ||||
| index b83f705..f533e2d 100644
 | ||||
| --- a/drivers/net/phy/sfp.h
 | ||||
| +++ b/drivers/net/phy/sfp.h
 | ||||
| @@ -6,6 +6,14 @@
 | ||||
| @@ -1551,7 +1647,7 @@ index b83f70526..f533e2dd6 100644 | ||||
|  int sfp_module_start(struct sfp_bus *bus); | ||||
|  void sfp_module_stop(struct sfp_bus *bus); | ||||
| diff --git a/include/linux/mdio/mdio-i2c.h b/include/linux/mdio/mdio-i2c.h
 | ||||
| index 751dab281..1c2114068 100644
 | ||||
| index 751dab2..1c21140 100644
 | ||||
| --- a/include/linux/mdio/mdio-i2c.h
 | ||||
| +++ b/include/linux/mdio/mdio-i2c.h
 | ||||
| @@ -11,6 +11,14 @@ struct device;
 | ||||
| @@ -1571,5 +1667,5 @@ index 751dab281..1c2114068 100644 | ||||
|   | ||||
|  #endif | ||||
| -- 
 | ||||
| 2.34.1 | ||||
| 2.45.2 | ||||
| 
 | ||||
| @@ -0,0 +1,109 @@ | ||||
| From d3fd7b4ccaf867811a777cf7aecaaa9f32d94331 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 13 May 2024 17:05:16 +0800 | ||||
| Subject: [PATCH] 999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable | ||||
|  | ||||
| --- | ||||
|  drivers/net/phy/phylink.c | 26 +++++++++++++++++++++++++- | ||||
|  include/linux/phylink.h   |  2 ++ | ||||
|  2 files changed, 27 insertions(+), 1 deletion(-) | ||||
|  | ||||
| diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c | ||||
| index 9b9bb17..1d79f59 100644 | ||||
| --- a/drivers/net/phy/phylink.c | ||||
| +++ b/drivers/net/phy/phylink.c | ||||
| @@ -32,6 +32,10 @@ | ||||
|  enum { | ||||
|  	PHYLINK_DISABLE_STOPPED, | ||||
|  	PHYLINK_DISABLE_LINK, | ||||
| + | ||||
| +	PCS_STATE_DOWN = 0, | ||||
| +	PCS_STATE_STARTING, | ||||
| +	PCS_STATE_STARTED, | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| @@ -69,6 +73,7 @@ struct phylink { | ||||
|  	struct mutex state_mutex; | ||||
|  	struct phylink_link_state phy_state; | ||||
|  	struct work_struct resolve; | ||||
| +	unsigned int pcs_state; | ||||
|   | ||||
|  	bool mac_link_dropped; | ||||
|  	bool using_mac_select_pcs; | ||||
| @@ -520,11 +525,20 @@ static void phylink_major_config(struct phylink *pl, bool restart, | ||||
|  	/* If we have a new PCS, switch to the new PCS after preparing the MAC | ||||
|  	 * for the change. | ||||
|  	 */ | ||||
| -	if (pcs_changed) | ||||
| +	if (pcs_changed) { | ||||
| +		if (pl->pcs && pl->pcs->ops->pcs_disable) | ||||
| +			pl->pcs->ops->pcs_disable(pl->pcs); | ||||
| + | ||||
|  		pl->pcs = pcs; | ||||
| +	} | ||||
|   | ||||
|  	phylink_mac_config(pl, state); | ||||
|   | ||||
| +	if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) { | ||||
| +		if (pl->pcs && pl->pcs->ops->pcs_enable) | ||||
| +			err = pl->pcs->ops->pcs_enable(pl->pcs); | ||||
| +	} | ||||
| + | ||||
|  	if (pl->pcs) { | ||||
|  		err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode, | ||||
|  					       state->interface, | ||||
| @@ -1022,6 +1036,7 @@ struct phylink *phylink_create(struct phylink_config *config, | ||||
|  	pl->link_config.speed = SPEED_UNKNOWN; | ||||
|  	pl->link_config.duplex = DUPLEX_UNKNOWN; | ||||
|  	pl->link_config.an_enabled = true; | ||||
| +	pl->pcs_state = PCS_STATE_DOWN; | ||||
|  	pl->mac_ops = mac_ops; | ||||
|  	__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); | ||||
|  	timer_setup(&pl->link_poll, phylink_fixed_poll, 0); | ||||
| @@ -1370,6 +1385,8 @@ void phylink_start(struct phylink *pl) | ||||
|  	if (pl->netdev) | ||||
|  		netif_carrier_off(pl->netdev); | ||||
|   | ||||
| +	pl->pcs_state = PCS_STATE_STARTING; | ||||
| + | ||||
|  	/* Apply the link configuration to the MAC when starting. This allows | ||||
|  	 * a fixed-link to start with the correct parameters, and also | ||||
|  	 * ensures that we set the appropriate advertisement for Serdes links. | ||||
| @@ -1381,6 +1398,8 @@ void phylink_start(struct phylink *pl) | ||||
|  	phylink_resolve_flow(pl, &pl->link_config); | ||||
|  	phylink_mac_initial_config(pl, true); | ||||
|   | ||||
| +	pl->pcs_state = PCS_STATE_STARTED; | ||||
| + | ||||
|  	clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); | ||||
|  	phylink_run_resolve(pl); | ||||
|   | ||||
| @@ -1442,6 +1461,11 @@ void phylink_stop(struct phylink *pl) | ||||
|  	} | ||||
|   | ||||
|  	phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); | ||||
| + | ||||
| +	pl->pcs_state = PCS_STATE_DOWN; | ||||
| + | ||||
| +	if (pl->pcs && pl->pcs->ops->pcs_disable) | ||||
| +		pl->pcs->ops->pcs_disable(pl->pcs); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(phylink_stop); | ||||
|   | ||||
| diff --git a/include/linux/phylink.h b/include/linux/phylink.h | ||||
| index e1c022f..fae9794 100644 | ||||
| --- a/include/linux/phylink.h | ||||
| +++ b/include/linux/phylink.h | ||||
| @@ -349,6 +349,8 @@ struct phylink_pcs { | ||||
|  struct phylink_pcs_ops { | ||||
|  	int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, | ||||
|  			    const struct phylink_link_state *state); | ||||
| +	int (*pcs_enable)(struct phylink_pcs *pcs); | ||||
| +	void (*pcs_disable)(struct phylink_pcs *pcs); | ||||
|  	void (*pcs_get_state)(struct phylink_pcs *pcs, | ||||
|  			      struct phylink_link_state *state); | ||||
|  	int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode, | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -0,0 +1,107 @@ | ||||
| From 83e56d18e57fc46c3a25f917dbd42fb9b1599ab1 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 1 Jul 2024 16:05:32 +0800 | ||||
| Subject: [PATCH]  | ||||
|  [backport-networking-drivers][999-1717-v5.12-net-phy-sfp-add-debugfs-support.patch] | ||||
|  | ||||
| --- | ||||
|  drivers/net/phy/sfp.c | 55 +++++++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 55 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c | ||||
| index d49a825..4bcc2bb 100644 | ||||
| --- a/drivers/net/phy/sfp.c | ||||
| +++ b/drivers/net/phy/sfp.c | ||||
| @@ -1,6 +1,7 @@ | ||||
|  // SPDX-License-Identifier: GPL-2.0 | ||||
|  #include <linux/acpi.h> | ||||
|  #include <linux/ctype.h> | ||||
| +#include <linux/debugfs.h> | ||||
|  #include <linux/delay.h> | ||||
|  #include <linux/gpio/consumer.h> | ||||
|  #include <linux/hwmon.h> | ||||
| @@ -268,6 +269,9 @@ struct sfp { | ||||
|  	char *hwmon_name; | ||||
|  #endif | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_DEBUG_FS) | ||||
| +	struct dentry *debugfs_dir; | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static bool sff_module_supported(const struct sfp_eeprom_id *id) | ||||
| @@ -1617,6 +1621,54 @@ static void sfp_module_tx_enable(struct sfp *sfp) | ||||
|  	sfp_set_state(sfp, sfp->state); | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_DEBUG_FS) | ||||
| +static int sfp_debug_state_show(struct seq_file *s, void *data) | ||||
| +{ | ||||
| +	struct sfp *sfp = s->private; | ||||
| + | ||||
| +	seq_printf(s, "Module state: %s\n", | ||||
| +		   mod_state_to_str(sfp->sm_mod_state)); | ||||
| +	seq_printf(s, "Module probe attempts: %d %d\n", | ||||
| +		   R_PROBE_RETRY_INIT - sfp->sm_mod_tries_init, | ||||
| +		   R_PROBE_RETRY_SLOW - sfp->sm_mod_tries); | ||||
| +	seq_printf(s, "Device state: %s\n", | ||||
| +		   dev_state_to_str(sfp->sm_dev_state)); | ||||
| +	seq_printf(s, "Main state: %s\n", | ||||
| +		   sm_state_to_str(sfp->sm_state)); | ||||
| +	seq_printf(s, "Fault recovery remaining retries: %d\n", | ||||
| +		   sfp->sm_fault_retries); | ||||
| +	seq_printf(s, "PHY probe remaining retries: %d\n", | ||||
| +		   sfp->sm_phy_retries); | ||||
| +	seq_printf(s, "moddef0: %d\n", !!(sfp->state & SFP_F_PRESENT)); | ||||
| +	seq_printf(s, "rx_los: %d\n", !!(sfp->state & SFP_F_LOS)); | ||||
| +	seq_printf(s, "tx_fault: %d\n", !!(sfp->state & SFP_F_TX_FAULT)); | ||||
| +	seq_printf(s, "tx_disable: %d\n", !!(sfp->state & SFP_F_TX_DISABLE)); | ||||
| +	return 0; | ||||
| +} | ||||
| +DEFINE_SHOW_ATTRIBUTE(sfp_debug_state); | ||||
| + | ||||
| +static void sfp_debugfs_init(struct sfp *sfp) | ||||
| +{ | ||||
| +	sfp->debugfs_dir = debugfs_create_dir(dev_name(sfp->dev), NULL); | ||||
| + | ||||
| +	debugfs_create_file("state", 0600, sfp->debugfs_dir, sfp, | ||||
| +			    &sfp_debug_state_fops); | ||||
| +} | ||||
| + | ||||
| +static void sfp_debugfs_exit(struct sfp *sfp) | ||||
| +{ | ||||
| +	debugfs_remove_recursive(sfp->debugfs_dir); | ||||
| +} | ||||
| +#else | ||||
| +static void sfp_debugfs_init(struct sfp *sfp) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
| +static void sfp_debugfs_exit(struct sfp *sfp) | ||||
| +{ | ||||
| +} | ||||
| +#endif | ||||
| + | ||||
|  static void sfp_module_tx_fault_reset(struct sfp *sfp) | ||||
|  { | ||||
|  	unsigned int state = sfp->state; | ||||
| @@ -2795,6 +2847,8 @@ static int sfp_probe(struct platform_device *pdev) | ||||
|  	if (!sfp->sfp_bus) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| +	sfp_debugfs_init(sfp); | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -2802,6 +2856,7 @@ static int sfp_remove(struct platform_device *pdev) | ||||
|  { | ||||
|  	struct sfp *sfp = platform_get_drvdata(pdev); | ||||
|   | ||||
| +	sfp_debugfs_exit(sfp); | ||||
|  	sfp_unregister_socket(sfp->sfp_bus); | ||||
|   | ||||
|  	rtnl_lock(); | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,202 @@ | ||||
| From 85fa9116d39817791f3da4dd642549db8916cb8e Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Tue, 30 Jan 2024 12:29:37 +0800 | ||||
| Subject: [PATCH] 999-1715-v6.2-net-ptp-introduce-adjust-by-scaled-ppm.patch | ||||
|  | ||||
| --- | ||||
|  drivers/ptp/ptp_clock.c                       | 21 ------ | ||||
|  include/linux/math64.h                        | 12 ++++ | ||||
|  include/linux/ptp_clock_kernel.h              | 72 +++++++++++++++++++ | ||||
|  lib/math/div64.c                              | 42 +++++++++++ | ||||
|  4 files changed, 126 insertions(+), 21 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c | ||||
| index eedf067..5cca99f 100644 | ||||
| --- a/drivers/ptp/ptp_clock.c | ||||
| +++ b/drivers/ptp/ptp_clock.c | ||||
| @@ -63,27 +63,6 @@ static void enqueue_external_timestamp(struct timestamp_event_queue *queue, | ||||
|  	spin_unlock_irqrestore(&queue->lock, flags); | ||||
|  } | ||||
|   | ||||
| -long scaled_ppm_to_ppb(long ppm) | ||||
| -{ | ||||
| -	/* | ||||
| -	 * The 'freq' field in the 'struct timex' is in parts per | ||||
| -	 * million, but with a 16 bit binary fractional field. | ||||
| -	 * | ||||
| -	 * We want to calculate | ||||
| -	 * | ||||
| -	 *    ppb = scaled_ppm * 1000 / 2^16 | ||||
| -	 * | ||||
| -	 * which simplifies to | ||||
| -	 * | ||||
| -	 *    ppb = scaled_ppm * 125 / 2^13 | ||||
| -	 */ | ||||
| -	s64 ppb = 1 + ppm; | ||||
| -	ppb *= 125; | ||||
| -	ppb >>= 13; | ||||
| -	return (long) ppb; | ||||
| -} | ||||
| -EXPORT_SYMBOL(scaled_ppm_to_ppb); | ||||
| - | ||||
|  /* posix clock implementation */ | ||||
|   | ||||
|  static int ptp_clock_getres(struct posix_clock *pc, struct timespec64 *tp) | ||||
| diff --git a/include/linux/math64.h b/include/linux/math64.h | ||||
| index 65bef21..a593096 100644 | ||||
| --- a/include/linux/math64.h | ||||
| +++ b/include/linux/math64.h | ||||
| @@ -281,6 +281,18 @@ static inline u64 mul_u64_u32_div(u64 a, u32 mul, u32 divisor) | ||||
|  } | ||||
|  #endif /* mul_u64_u32_div */ | ||||
|   | ||||
| +u64 mul_u64_u64_div_u64(u64 a, u64 mul, u64 div); | ||||
| + | ||||
| +/** | ||||
| + * DIV64_U64_ROUND_UP - unsigned 64bit divide with 64bit divisor rounded up | ||||
| + * @ll: unsigned 64bit dividend | ||||
| + * @d: unsigned 64bit divisor | ||||
| + * | ||||
| + * Divide unsigned 64bit dividend by unsigned 64bit divisor | ||||
| + * and round up. | ||||
| + * | ||||
| + * Return: dividend / divisor rounded up | ||||
| + */ | ||||
|  #define DIV64_U64_ROUND_UP(ll, d)	\ | ||||
|  	({ u64 _tmp = (d); div64_u64((ll) + _tmp - 1, _tmp); }) | ||||
|   | ||||
| diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h | ||||
| index 874f7e7..2ff9afe 100644 | ||||
| --- a/include/linux/ptp_clock_kernel.h | ||||
| +++ b/include/linux/ptp_clock_kernel.h | ||||
| @@ -169,6 +169,78 @@ struct ptp_clock_event { | ||||
|  	}; | ||||
|  }; | ||||
|   | ||||
| +/** | ||||
| + * scaled_ppm_to_ppb() - convert scaled ppm to ppb | ||||
| + * | ||||
| + * @ppm:    Parts per million, but with a 16 bit binary fractional field | ||||
| + */ | ||||
| +static inline long scaled_ppm_to_ppb(long ppm) | ||||
| +{ | ||||
| +	/* | ||||
| +	 * The 'freq' field in the 'struct timex' is in parts per | ||||
| +	 * million, but with a 16 bit binary fractional field. | ||||
| +	 * | ||||
| +	 * We want to calculate | ||||
| +	 * | ||||
| +	 *    ppb = scaled_ppm * 1000 / 2^16 | ||||
| +	 * | ||||
| +	 * which simplifies to | ||||
| +	 * | ||||
| +	 *    ppb = scaled_ppm * 125 / 2^13 | ||||
| +	 */ | ||||
| +	s64 ppb = 1 + ppm; | ||||
| + | ||||
| +	ppb *= 125; | ||||
| +	ppb >>= 13; | ||||
| +	return (long)ppb; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * diff_by_scaled_ppm - Calculate difference using scaled ppm | ||||
| + * @base: the base increment value to adjust | ||||
| + * @scaled_ppm: scaled parts per million to adjust by | ||||
| + * @diff: on return, the absolute value of calculated diff | ||||
| + * | ||||
| + * Calculate the difference to adjust the base increment using scaled parts | ||||
| + * per million. | ||||
| + * | ||||
| + * Use mul_u64_u64_div_u64 to perform the difference calculation in avoid | ||||
| + * possible overflow. | ||||
| + * | ||||
| + * Returns: true if scaled_ppm is negative, false otherwise | ||||
| + */ | ||||
| +static inline bool diff_by_scaled_ppm(u64 base, long scaled_ppm, u64 *diff) | ||||
| +{ | ||||
| +	bool negative = false; | ||||
| + | ||||
| +	if (scaled_ppm < 0) { | ||||
| +		negative = true; | ||||
| +		scaled_ppm = -scaled_ppm; | ||||
| +	} | ||||
| + | ||||
| +	*diff = mul_u64_u64_div_u64(base, (u64)scaled_ppm, 1000000ULL << 16); | ||||
| + | ||||
| +	return negative; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adjust_by_scaled_ppm - Adjust a base increment by scaled parts per million | ||||
| + * @base: the base increment value to adjust | ||||
| + * @scaled_ppm: scaled parts per million frequency adjustment | ||||
| + * | ||||
| + * Helper function which calculates a new increment value based on the | ||||
| + * requested scaled parts per million adjustment. | ||||
| + */ | ||||
| +static inline u64 adjust_by_scaled_ppm(u64 base, long scaled_ppm) | ||||
| +{ | ||||
| +	u64 diff; | ||||
| + | ||||
| +	if (diff_by_scaled_ppm(base, scaled_ppm, &diff)) | ||||
| +		return base - diff; | ||||
| + | ||||
| +	return base + diff; | ||||
| +} | ||||
| + | ||||
|  #if IS_REACHABLE(CONFIG_PTP_1588_CLOCK) | ||||
|   | ||||
|  /** | ||||
| diff --git a/lib/math/div64.c b/lib/math/div64.c | ||||
| index 368ca7f..edd1090 100644 | ||||
| --- a/lib/math/div64.c | ||||
| +++ b/lib/math/div64.c | ||||
| @@ -190,3 +190,45 @@ u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder) | ||||
|  	return __iter_div_u64_rem(dividend, divisor, remainder); | ||||
|  } | ||||
|  EXPORT_SYMBOL(iter_div_u64_rem); | ||||
| + | ||||
| +#ifndef mul_u64_u64_div_u64 | ||||
| +u64 mul_u64_u64_div_u64(u64 a, u64 b, u64 c) | ||||
| +{ | ||||
| +	u64 res = 0, div, rem; | ||||
| +	int shift; | ||||
| + | ||||
| +	/* can a * b overflow ? */ | ||||
| +	if (ilog2(a) + ilog2(b) > 62) { | ||||
| +		/* | ||||
| +		 * (b * a) / c is equal to | ||||
| +		 * | ||||
| +		 *      (b / c) * a + | ||||
| +		 *      (b % c) * a / c | ||||
| +		 * | ||||
| +		 * if nothing overflows. Can the 1st multiplication | ||||
| +		 * overflow? Yes, but we do not care: this can only | ||||
| +		 * happen if the end result can't fit in u64 anyway. | ||||
| +		 * | ||||
| +		 * So the code below does | ||||
| +		 * | ||||
| +		 *      res = (b / c) * a; | ||||
| +		 *      b = b % c; | ||||
| +		 */ | ||||
| +		div = div64_u64_rem(b, c, &rem); | ||||
| +		res = div * a; | ||||
| +		b = rem; | ||||
| + | ||||
| +		shift = ilog2(a) + ilog2(b) - 62; | ||||
| +		if (shift > 0) { | ||||
| +			/* drop precision */ | ||||
| +			b >>= shift; | ||||
| +			c >>= shift; | ||||
| +			if (!c) | ||||
| +				return res; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	return res + div64_u64(a * b, c); | ||||
| +} | ||||
| +EXPORT_SYMBOL(mul_u64_u64_div_u64); | ||||
| +#endif | ||||
| --  | ||||
| 2.18.0 | ||||
| @@ -0,0 +1,80 @@ | ||||
| From 6afacf5f918314789d95739311978b596ea46384 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Tue, 19 Nov 2024 11:53:53 +0800 | ||||
| Subject: [PATCH] net: phy: add genphy_c45_pma_suspend/resume | ||||
|  | ||||
| --- | ||||
|  drivers/net/phy/phy-c45.c | 43 +++++++++++++++++++++++++++++++++++++++ | ||||
|  include/linux/phy.h       |  2 ++ | ||||
|  2 files changed, 45 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c | ||||
| index bceb0dc..797946c 100644 | ||||
| --- a/drivers/net/phy/phy-c45.c | ||||
| +++ b/drivers/net/phy/phy-c45.c | ||||
| @@ -8,6 +8,49 @@ | ||||
|  #include <linux/mii.h> | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
| +/** | ||||
| + * genphy_c45_pma_can_sleep - checks if the PMA have sleep support | ||||
| + * @phydev: target phy_device struct | ||||
| + */ | ||||
| +static bool genphy_c45_pma_can_sleep(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int stat1; | ||||
| + | ||||
| +	stat1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT1); | ||||
| +	if (stat1 < 0) | ||||
| +		return false; | ||||
| + | ||||
| +	return !!(stat1 & MDIO_STAT1_LPOWERABLE); | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * genphy_c45_pma_resume - wakes up the PMA module | ||||
| + * @phydev: target phy_device struct | ||||
| + */ | ||||
| +int genphy_c45_pma_resume(struct phy_device *phydev) | ||||
| +{ | ||||
| +	if (!genphy_c45_pma_can_sleep(phydev)) | ||||
| +		return -EOPNOTSUPP; | ||||
| + | ||||
| +	return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, | ||||
| +				  MDIO_CTRL1_LPOWER); | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(genphy_c45_pma_resume); | ||||
| + | ||||
| +/** | ||||
| + * genphy_c45_pma_suspend - suspends the PMA module | ||||
| + * @phydev: target phy_device struct | ||||
| + */ | ||||
| +int genphy_c45_pma_suspend(struct phy_device *phydev) | ||||
| +{ | ||||
| +	if (!genphy_c45_pma_can_sleep(phydev)) | ||||
| +		return -EOPNOTSUPP; | ||||
| + | ||||
| +	return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, | ||||
| +				MDIO_CTRL1_LPOWER); | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(genphy_c45_pma_suspend); | ||||
| + | ||||
|  /** | ||||
|   * genphy_c45_setup_forced - configures a forced speed | ||||
|   * @phydev: target phy_device struct | ||||
| diff --git a/include/linux/phy.h b/include/linux/phy.h | ||||
| index 83bd00b..fb21ddb 100644 | ||||
| --- a/include/linux/phy.h | ||||
| +++ b/include/linux/phy.h | ||||
| @@ -1232,6 +1232,8 @@ int genphy_c45_read_mdix(struct phy_device *phydev); | ||||
|  int genphy_c45_pma_read_abilities(struct phy_device *phydev); | ||||
|  int genphy_c45_read_status(struct phy_device *phydev); | ||||
|  int genphy_c45_config_aneg(struct phy_device *phydev); | ||||
| +int genphy_c45_pma_resume(struct phy_device *phydev); | ||||
| +int genphy_c45_pma_suspend(struct phy_device *phydev); | ||||
|   | ||||
|  /* The gen10g_* functions are the old Clause 45 stub */ | ||||
|  int gen10g_config_aneg(struct phy_device *phydev); | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -0,0 +1,28 @@ | ||||
| From d1d8518eebcb82ba7b0ee5f34c05a730da125588 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Tue, 19 Nov 2024 12:27:04 +0800 | ||||
| Subject: [PATCH] net: phy: add 2.5g and 5g related PMA and PCS speed constants | ||||
|  | ||||
| --- | ||||
|  include/uapi/linux/mdio.h | 4 ++++ | ||||
|  1 file changed, 4 insertions(+) | ||||
|  | ||||
| diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h | ||||
| index 3f302e2..353a146 100644 | ||||
| --- a/include/uapi/linux/mdio.h | ||||
| +++ b/include/uapi/linux/mdio.h | ||||
| @@ -119,7 +119,11 @@ | ||||
|  #define MDIO_PMA_SPEED_1000		0x0010	/* 1000M capable */ | ||||
|  #define MDIO_PMA_SPEED_100		0x0020	/* 100M capable */ | ||||
|  #define MDIO_PMA_SPEED_10		0x0040	/* 10M capable */ | ||||
| +#define MDIO_PMA_SPEED_2_5G		0x2000	/* 2.5G capable */ | ||||
| +#define MDIO_PMA_SPEED_5G		0x4000	/* 5G capable */ | ||||
|  #define MDIO_PCS_SPEED_10P2B		0x0002	/* 10PASS-TS/2BASE-TL capable */ | ||||
| +#define MDIO_PCS_SPEED_2_5G		0x0040	/* 2.5G capable */ | ||||
| +#define MDIO_PCS_SPEED_5G		0x0080	/* 5G capable */ | ||||
|   | ||||
|  /* Device present registers. */ | ||||
|  #define MDIO_DEVS_PRESENT(devad)	(1 << (devad)) | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -0,0 +1,605 @@ | ||||
| From 9e9eecff8085737bb81f279f9283f5d67e3b5e29 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Tue, 19 Nov 2024 12:49:36 +0800 | ||||
| Subject: [PATCH] net: phy: realtek add rtl8221b series | ||||
|  | ||||
| --- | ||||
|  drivers/net/phy/realtek.c | 539 ++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 539 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c | ||||
| index 879ca37..b228b6d 100644 | ||||
| --- a/drivers/net/phy/realtek.c | ||||
| +++ b/drivers/net/phy/realtek.c | ||||
| @@ -9,6 +9,7 @@ | ||||
|   * Copyright (c) 2004 Freescale Semiconductor, Inc. | ||||
|   */ | ||||
|  #include <linux/bitops.h> | ||||
| +#include <linux/of.h> | ||||
|  #include <linux/phy.h> | ||||
|  #include <linux/module.h> | ||||
|   | ||||
| @@ -36,6 +37,27 @@ | ||||
|  #define RTL8201F_ISR				0x1e | ||||
|  #define RTL8201F_IER				0x13 | ||||
|   | ||||
| +#define RTL822X_VND1_SERDES_OPTION			0x697a | ||||
| +#define RTL822X_VND1_SERDES_OPTION_MODE_MASK		GENMASK(5, 0) | ||||
| +#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII		0 | ||||
| +#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX		2 | ||||
| + | ||||
| +#define RTL822X_VND1_SERDES_CTRL3			0x7580 | ||||
| +#define RTL822X_VND1_SERDES_CTRL3_MODE_MASK		GENMASK(5, 0) | ||||
| +#define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII			0x02 | ||||
| +#define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX		0x16 | ||||
| + | ||||
| +/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45 | ||||
| + * is set, they cannot be accessed by C45-over-C22. | ||||
| + */ | ||||
| +#define RTL822X_VND2_GBCR				0xa412 | ||||
| + | ||||
| +#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) | ||||
|   | ||||
| @@ -47,7 +69,16 @@ | ||||
|  #define RTL_LPADV_5000FULL			BIT(6) | ||||
|  #define RTL_LPADV_2500FULL			BIT(5) | ||||
|   | ||||
| +#define RTL_VND2_PHYSR				0xa434 | ||||
| +#define RTL_VND2_PHYSR_DUPLEX			BIT(3) | ||||
| +#define RTL_VND2_PHYSR_SPEEDL			GENMASK(5, 4) | ||||
| +#define RTL_VND2_PHYSR_SPEEDH			GENMASK(10, 9) | ||||
| +#define RTL_VND2_PHYSR_MASTER			BIT(11) | ||||
| +#define RTL_VND2_PHYSR_SPEED_MASK		(RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH) | ||||
| + | ||||
|  #define RTL_GENERIC_PHYID			0x001cc800 | ||||
| +#define RTL_8221B_VB_CG				0x001cc849 | ||||
| +#define RTL_8221B_VN_CG				0x001cc84a | ||||
|   | ||||
|  MODULE_DESCRIPTION("Realtek PHY driver"); | ||||
|  MODULE_AUTHOR("Johnson Leung"); | ||||
| @@ -283,6 +314,62 @@ static int rtl8366rb_config_init(struct phy_device *phydev) | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +/* get actual speed to cover the downshift case */ | ||||
| +static void rtlgen_decode_physr(struct phy_device *phydev, int val) | ||||
| +{ | ||||
| +	/* bit 3 | ||||
| +	 * 0: Half Duplex | ||||
| +	 * 1: Full Duplex | ||||
| +	 */ | ||||
| +	if (val & RTL_VND2_PHYSR_DUPLEX) | ||||
| +		phydev->duplex = DUPLEX_FULL; | ||||
| +	else | ||||
| +		phydev->duplex = DUPLEX_HALF; | ||||
| + | ||||
| +	switch (val & RTL_VND2_PHYSR_SPEED_MASK) { | ||||
| +	case 0x0000: | ||||
| +		phydev->speed = SPEED_10; | ||||
| +		break; | ||||
| +	case 0x0010: | ||||
| +		phydev->speed = SPEED_100; | ||||
| +		break; | ||||
| +	case 0x0020: | ||||
| +		phydev->speed = SPEED_1000; | ||||
| +		break; | ||||
| +	case 0x0200: | ||||
| +		phydev->speed = SPEED_10000; | ||||
| +		break; | ||||
| +	case 0x0210: | ||||
| +		phydev->speed = SPEED_2500; | ||||
| +		break; | ||||
| +	case 0x0220: | ||||
| +		phydev->speed = SPEED_5000; | ||||
| +		break; | ||||
| +	default: | ||||
| +		break; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static int rtlgen_read_status(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret, val; | ||||
| + | ||||
| +	ret = genphy_read_status(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	if (!phydev->link) | ||||
| +		return 0; | ||||
| + | ||||
| +	val = phy_read_paged(phydev, 0xa43, 0x12); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	rtlgen_decode_physr(phydev, val); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) | ||||
|  { | ||||
|  	int ret; | ||||
| @@ -420,6 +507,266 @@ static int rtl8125_read_status(struct phy_device *phydev) | ||||
|  	return genphy_read_status(phydev); | ||||
|  } | ||||
|   | ||||
| +static int rtl822xb_config_init(struct phy_device *phydev) | ||||
| +{ | ||||
| +	bool has_2500 = true, has_sgmii = false; | ||||
| +	int ret, val; | ||||
| +	u16 mode; | ||||
| + | ||||
| +	/* determine SerDes option mode */ | ||||
| +	if (has_2500 && !has_sgmii) { | ||||
| +		mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX; | ||||
| +		phydev->rate_matching = RATE_MATCH_PAUSE; | ||||
| +	} else { | ||||
| +		mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII; | ||||
| +		phydev->rate_matching = RATE_MATCH_NONE; | ||||
| +	} | ||||
| + | ||||
| +	/* the following sequence with magic numbers sets up the SerDes | ||||
| +	 * option mode | ||||
| +	 */ | ||||
| +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1, | ||||
| +				     RTL822X_VND1_SERDES_OPTION, | ||||
| +				     RTL822X_VND1_SERDES_OPTION_MODE_MASK, | ||||
| +				     mode); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* Disable SGMII AN */ | ||||
| +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7588, 0x2); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7589, 0x71d0); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7587, 0x3); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, 0x7587, | ||||
| +					val, !(val & BIT(0)), 500, 100000, false); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int rtl822xb_get_rate_matching(struct phy_device *phydev, | ||||
| +				      phy_interface_t iface) | ||||
| +{ | ||||
| +	int val; | ||||
| + | ||||
| +	/* Only rate matching at 2500base-x */ | ||||
| +	if (iface != PHY_INTERFACE_MODE_2500BASEX) | ||||
| +		return RATE_MATCH_NONE; | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_OPTION); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	if ((val & RTL822X_VND1_SERDES_OPTION_MODE_MASK) == | ||||
| +	    RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX) | ||||
| +		return RATE_MATCH_PAUSE; | ||||
| + | ||||
| +	/* RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII */ | ||||
| +	return RATE_MATCH_NONE; | ||||
| +} | ||||
| + | ||||
| +static int rtl822x_get_features(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int val; | ||||
| + | ||||
| +	val = phy_read_paged(phydev, 0xa61, 0x13); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, | ||||
| +			 phydev->supported, val & MDIO_PMA_SPEED_2_5G); | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, | ||||
| +			 phydev->supported, val & MDIO_PMA_SPEED_5G); | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, | ||||
| +			 phydev->supported, val & MDIO_SPEED_10G); | ||||
| + | ||||
| +	return genphy_read_abilities(phydev); | ||||
| +} | ||||
| + | ||||
| +static int rtl822x_config_aneg(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret = 0; | ||||
| + | ||||
| +	if (phydev->autoneg == AUTONEG_ENABLE) { | ||||
| +		u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); | ||||
| + | ||||
| +		ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12, | ||||
| +					       MDIO_AN_10GBT_CTRL_ADV2_5G | | ||||
| +					       MDIO_AN_10GBT_CTRL_ADV5G, | ||||
| +					       adv); | ||||
| +		if (ret < 0) | ||||
| +			return ret; | ||||
| +	} | ||||
| + | ||||
| +	return __genphy_config_aneg(phydev, ret); | ||||
| +} | ||||
| + | ||||
| +static void rtl822xb_update_interface(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int val; | ||||
| + | ||||
| +	if (!phydev->link) | ||||
| +		return; | ||||
| + | ||||
| +	/* Change interface according to serdes mode */ | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CTRL3); | ||||
| +	if (val < 0) | ||||
| +		return; | ||||
| + | ||||
| +	switch (val & RTL822X_VND1_SERDES_CTRL3_MODE_MASK) { | ||||
| +	case RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX: | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_2500BASEX; | ||||
| +		break; | ||||
| +	case RTL822X_VND1_SERDES_CTRL3_MODE_SGMII: | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_SGMII; | ||||
| +		break; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static int rtl822x_read_status(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int lpadv, ret; | ||||
| + | ||||
| +	ret = rtlgen_read_status(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	if (phydev->autoneg == AUTONEG_DISABLE || | ||||
| +	    !phydev->autoneg_complete) { | ||||
| +		mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); | ||||
| +		return 0; | ||||
| +	} | ||||
| + | ||||
| +	lpadv = phy_read_paged(phydev, 0xa5d, 0x13); | ||||
| +	if (lpadv < 0) | ||||
| +		return lpadv; | ||||
| + | ||||
| +	mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int rtl822xb_read_status(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = rtl822x_read_status(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	rtl822xb_update_interface(phydev); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int rtl822x_c45_get_features(struct phy_device *phydev) | ||||
| +{ | ||||
| +	linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, | ||||
| +			 phydev->supported); | ||||
| + | ||||
| +	return genphy_c45_pma_read_abilities(phydev); | ||||
| +} | ||||
| + | ||||
| +static int rtl822x_c45_config_aneg(struct phy_device *phydev) | ||||
| +{ | ||||
| +	bool changed = false; | ||||
| +	int ret, val; | ||||
| + | ||||
| +	if (phydev->autoneg == AUTONEG_DISABLE) | ||||
| +		return genphy_c45_pma_setup_forced(phydev); | ||||
| + | ||||
| +	ret = genphy_c45_an_config_aneg(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| +	if (ret > 0) | ||||
| +		changed = true; | ||||
| + | ||||
| +	val = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); | ||||
| + | ||||
| +	/* Vendor register as C45 has no standardized support for 1000BaseT */ | ||||
| +	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822X_VND2_GBCR, | ||||
| +				     ADVERTISE_1000FULL, val); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| +	if (ret > 0) | ||||
| +		changed = true; | ||||
| + | ||||
| +	return genphy_c45_check_and_restart_aneg(phydev, changed); | ||||
| +} | ||||
| + | ||||
| +static int rtl822x_c45_read_status(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret, val; | ||||
| + | ||||
| +	ret = genphy_c45_read_status(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	if (phydev->autoneg == AUTONEG_DISABLE || | ||||
| +	    !genphy_c45_aneg_done(phydev)) | ||||
| +		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 0); | ||||
| + | ||||
| +	/* Vendor register as C45 has no standardized support for 1000BaseT */ | ||||
| +	if (phydev->autoneg == AUTONEG_ENABLE) { | ||||
| +		val = phy_read_mmd(phydev, MDIO_MMD_VEND2, | ||||
| +				   RTL822X_VND2_GANLPAR); | ||||
| +		if (val < 0) | ||||
| +			return val; | ||||
| + | ||||
| +		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); | ||||
| +	} | ||||
| + | ||||
| +	if (!phydev->link) | ||||
| +		return 0; | ||||
| + | ||||
| +	/* Read actual speed from vendor register. */ | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	rtlgen_decode_physr(phydev, val); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int rtl822xb_c45_read_status(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = rtl822x_c45_read_status(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	rtl822xb_update_interface(phydev); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) | ||||
|  { | ||||
|  	int val; | ||||
| @@ -443,6 +790,138 @@ static int rtl8125_match_phy_device(struct phy_device *phydev) | ||||
|  	       rtlgen_supports_2_5gbps(phydev); | ||||
|  } | ||||
|   | ||||
| +static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, | ||||
| +			       bool is_c45) | ||||
| +{ | ||||
| +	if (phydev->is_c45) { | ||||
| +		u32 rid; | ||||
| + | ||||
| +		if (!is_c45) | ||||
| +			return 0; | ||||
| + | ||||
| +		rid = phydev->c45_ids.device_ids[1]; | ||||
| +		if ((rid == 0xffffffff) && phydev->mdio.bus->read) { | ||||
| +			int val; | ||||
| + | ||||
| +			val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID1); | ||||
| +			if (val < 0) | ||||
| +				return 0; | ||||
| + | ||||
| +			rid = val << 16; | ||||
| +			val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID2); | ||||
| +			if (val < 0) | ||||
| +				return 0; | ||||
| + | ||||
| +			rid |= val; | ||||
| +		} | ||||
| + | ||||
| +		return (id == rid); | ||||
| +	} else { | ||||
| +		return !is_c45 && (id == phydev->phy_id); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) | ||||
| +{ | ||||
| +	return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); | ||||
| +} | ||||
| + | ||||
| +static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) | ||||
| +{ | ||||
| +	return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); | ||||
| +} | ||||
| + | ||||
| +static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) | ||||
| +{ | ||||
| +	return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); | ||||
| +} | ||||
| + | ||||
| +static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) | ||||
| +{ | ||||
| +	return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, 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); | ||||
| + | ||||
| +	/* Internal PHY's from RTL8168h up may not be instantly ready */ | ||||
| +	msleep(20); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static int rtlgen_c45_resume(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret = genphy_c45_pma_resume(phydev); | ||||
| + | ||||
| +	msleep(20); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static int rtl8221b_ack_interrupt(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int err; | ||||
| + | ||||
| +	err = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa4d4); | ||||
| + | ||||
| +	return (err < 0) ? err : 0; | ||||
| +} | ||||
| + | ||||
| +static int rtl8221b_config_intr(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int err; | ||||
| + | ||||
| +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { | ||||
| +		err = rtl8221b_ack_interrupt(phydev); | ||||
| +		if (err) | ||||
| +			return err; | ||||
| + | ||||
| +		err = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xa4d2, 0x7ff); | ||||
| +	} else { | ||||
| +		err = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xa4d2, 0x0); | ||||
| +		if (err) | ||||
| +			return err; | ||||
| + | ||||
| +		err = rtl8221b_ack_interrupt(phydev); | ||||
| +	} | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int rtl8221b_handle_interrupt(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int err; | ||||
| + | ||||
| +	err = rtl8221b_ack_interrupt(phydev); | ||||
| +	if (err) | ||||
| +		return -1; | ||||
| + | ||||
| +	phy_queue_state_machine(phydev, 0); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static struct phy_driver realtek_drvs[] = { | ||||
|  	{ | ||||
|  		PHY_ID_MATCH_EXACT(0x00008201), | ||||
| @@ -542,6 +1021,66 @@ static struct phy_driver realtek_drvs[] = { | ||||
|  		.write_page	= rtl821x_write_page, | ||||
|  		.read_mmd	= rtl8125_read_mmd, | ||||
|  		.write_mmd	= rtl8125_write_mmd, | ||||
| +	}, { | ||||
| +		.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, | ||||
| +		.get_features   = rtl822x_get_features, | ||||
| +		.config_aneg    = rtl822x_config_aneg, | ||||
| +		.config_init    = rtl822xb_config_init, | ||||
| +		.get_rate_matching = rtl822xb_get_rate_matching, | ||||
| +		.read_status    = rtl822xb_read_status, | ||||
| +		.suspend        = genphy_suspend, | ||||
| +		.resume         = rtlgen_resume, | ||||
| +		.read_page      = rtl821x_read_page, | ||||
| +		.write_page     = rtl821x_write_page, | ||||
| +	}, { | ||||
| +		.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,*/ | ||||
| +		.config_init    = rtl822xb_config_init, | ||||
| +		.get_rate_matching = rtl822xb_get_rate_matching, | ||||
| +		.get_features   = rtl822x_c45_get_features, | ||||
| +		.config_aneg    = rtl822x_c45_config_aneg, | ||||
| +		.read_status    = rtl822xb_c45_read_status, | ||||
| +		.suspend        = genphy_c45_pma_suspend, | ||||
| +		.resume         = rtlgen_c45_resume, | ||||
| +	}, { | ||||
| +		.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, | ||||
| +		.get_features   = rtl822x_get_features, | ||||
| +		.config_aneg    = rtl822x_config_aneg, | ||||
| +		.config_init    = rtl822xb_config_init, | ||||
| +		.get_rate_matching = rtl822xb_get_rate_matching, | ||||
| +		.read_status    = rtl822xb_read_status, | ||||
| +		.suspend        = genphy_suspend, | ||||
| +		.resume         = rtlgen_resume, | ||||
| +		.read_page      = rtl821x_read_page, | ||||
| +		.write_page     = rtl821x_write_page, | ||||
| +	}, { | ||||
| +		.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, | ||||
| +		.config_init    = rtl822xb_config_init, | ||||
| +		.get_rate_matching = rtl822xb_get_rate_matching, | ||||
| +		.get_features   = rtl822x_c45_get_features, | ||||
| +		.config_aneg    = rtl822x_c45_config_aneg, | ||||
| +		.read_status    = rtl822xb_c45_read_status, | ||||
| +		.suspend        = genphy_c45_pma_suspend, | ||||
| +		.resume         = rtlgen_c45_resume, | ||||
|  	}, { | ||||
|  		PHY_ID_MATCH_EXACT(0x001cc961), | ||||
|  		.name		= "RTL8366RB Gigabit Ethernet", | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -0,0 +1,34 @@ | ||||
| From de404873748fcfc3a2c281851f0cc57664ceea70 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 12 May 2025 15:47:16 +0800 | ||||
| Subject: [PATCH] net: phy: add sanity check to read/write mmd | ||||
|  | ||||
| --- | ||||
|  drivers/net/phy/phy-core.c | 4 ++-- | ||||
|  1 file changed, 2 insertions(+), 2 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c | ||||
| index c34fc07..f160536 100644 | ||||
| --- a/drivers/net/phy/phy-core.c | ||||
| +++ b/drivers/net/phy/phy-core.c | ||||
| @@ -400,7 +400,7 @@ int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) | ||||
|  	if (regnum > (u16)~0 || devad > 32) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	if (phydev->drv->read_mmd) { | ||||
| +	if (phydev->drv && phydev->drv->read_mmd) { | ||||
|  		val = phydev->drv->read_mmd(phydev, devad, regnum); | ||||
|  	} else if (phydev->is_c45) { | ||||
|  		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); | ||||
| @@ -457,7 +457,7 @@ int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) | ||||
|  	if (regnum > (u16)~0 || devad > 32) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	if (phydev->drv->write_mmd) { | ||||
| +	if (phydev->drv && phydev->drv->write_mmd) { | ||||
|  		ret = phydev->drv->write_mmd(phydev, devad, regnum, val); | ||||
|  	} else if (phydev->is_c45) { | ||||
|  		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -0,0 +1,57 @@ | ||||
| diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c | ||||
| index 7f7cdbd..0fd9885 100644 | ||||
| --- a/drivers/net/phy/phy-core.c | ||||
| +++ b/drivers/net/phy/phy-core.c | ||||
| @@ -8,7 +8,7 @@ | ||||
|   | ||||
|  const char *phy_speed_to_str(int speed) | ||||
|  { | ||||
| -	BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 69, | ||||
| +	BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 102, | ||||
|  		"Enum ethtool_link_mode_bit_indices and phylib are out of sync. " | ||||
|  		"If a speed or mode has been added please update phy_speed_to_str " | ||||
|  		"and the PHY settings array.\n"); | ||||
| diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h | ||||
| index ce41d2f..502baa2 100644 | ||||
| --- a/include/uapi/linux/ethtool.h | ||||
| +++ b/include/uapi/linux/ethtool.h | ||||
| @@ -1507,6 +1507,39 @@ enum ethtool_link_mode_bit_indices { | ||||
|  	ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT	 = 66, | ||||
|  	ETHTOOL_LINK_MODE_100baseT1_Full_BIT		 = 67, | ||||
|  	ETHTOOL_LINK_MODE_1000baseT1_Full_BIT		 = 68, | ||||
| +	ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT	 = 69, | ||||
| +	ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT	 = 70, | ||||
| +	ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71, | ||||
| +	ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT	 = 72, | ||||
| +	ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT	 = 73, | ||||
| +	ETHTOOL_LINK_MODE_FEC_LLRS_BIT			 = 74, | ||||
| +	ETHTOOL_LINK_MODE_100000baseKR_Full_BIT		 = 75, | ||||
| +	ETHTOOL_LINK_MODE_100000baseSR_Full_BIT		 = 76, | ||||
| +	ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT	 = 77, | ||||
| +	ETHTOOL_LINK_MODE_100000baseCR_Full_BIT		 = 78, | ||||
| +	ETHTOOL_LINK_MODE_100000baseDR_Full_BIT		 = 79, | ||||
| +	ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT	 = 80, | ||||
| +	ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT	 = 81, | ||||
| +	ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82, | ||||
| +	ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT	 = 83, | ||||
| +	ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT	 = 84, | ||||
| +	ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT	 = 85, | ||||
| +	ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT	 = 86, | ||||
| +	ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87, | ||||
| +	ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT	 = 88, | ||||
| +	ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT	 = 89, | ||||
| +	ETHTOOL_LINK_MODE_100baseFX_Half_BIT		 = 90, | ||||
| +	ETHTOOL_LINK_MODE_100baseFX_Full_BIT		 = 91, | ||||
| +	ETHTOOL_LINK_MODE_10baseT1L_Full_BIT		 = 92, | ||||
| +	ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT	 = 93, | ||||
| +	ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT	 = 94, | ||||
| +	ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT	 = 95, | ||||
| +	ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT	 = 96, | ||||
| +	ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT	 = 97, | ||||
| +	ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT	 = 98, | ||||
| +	ETHTOOL_LINK_MODE_10baseT1S_Full_BIT		 = 99, | ||||
| +	ETHTOOL_LINK_MODE_10baseT1S_Half_BIT		 = 100, | ||||
| +	ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT	 = 101, | ||||
|   | ||||
|  	/* must be last entry */ | ||||
|  	__ETHTOOL_LINK_MODE_MASK_NBITS | ||||
| @@ -0,0 +1,394 @@ | ||||
| diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c | ||||
| index 797946c..4bb258b 100644 | ||||
| --- a/drivers/net/phy/phy-c45.c | ||||
| +++ b/drivers/net/phy/phy-c45.c | ||||
| @@ -8,6 +8,25 @@ | ||||
|  #include <linux/mii.h> | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
| +/** | ||||
| + * genphy_c45_baset1_able - checks if the PMA has BASE-T1 extended abilities | ||||
| + * @phydev: target phy_device struct | ||||
| + */ | ||||
| +static bool genphy_c45_baset1_able(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int val; | ||||
| + | ||||
| +	if (phydev->pma_extable == -ENODATA) { | ||||
| +		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); | ||||
| +		if (val < 0) | ||||
| +			return false; | ||||
| + | ||||
| +		phydev->pma_extable = val; | ||||
| +	} | ||||
| + | ||||
| +	return !!(phydev->pma_extable & MDIO_PMA_EXTABLE_BT1); | ||||
| +} | ||||
| + | ||||
|  /** | ||||
|   * genphy_c45_pma_can_sleep - checks if the PMA have sleep support | ||||
|   * @phydev: target phy_device struct | ||||
| @@ -52,7 +71,7 @@ int genphy_c45_pma_suspend(struct phy_device *phydev) | ||||
|  EXPORT_SYMBOL_GPL(genphy_c45_pma_suspend); | ||||
|   | ||||
|  /** | ||||
| - * genphy_c45_setup_forced - configures a forced speed | ||||
| + * genphy_c45_pma_setup_forced - configures a forced speed | ||||
|   * @phydev: target phy_device struct | ||||
|   */ | ||||
|  int genphy_c45_pma_setup_forced(struct phy_device *phydev) | ||||
| @@ -178,8 +197,12 @@ EXPORT_SYMBOL_GPL(genphy_c45_an_config_aneg); | ||||
|   */ | ||||
|  int genphy_c45_an_disable_aneg(struct phy_device *phydev) | ||||
|  { | ||||
| +	u16 reg = MDIO_CTRL1; | ||||
|   | ||||
| -	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, | ||||
| +	if (genphy_c45_baset1_able(phydev)) | ||||
| +		reg = MDIO_AN_T1_CTRL; | ||||
| + | ||||
| +	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, | ||||
|  				  MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg); | ||||
| @@ -194,7 +217,12 @@ EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg); | ||||
|   */ | ||||
|  int genphy_c45_restart_aneg(struct phy_device *phydev) | ||||
|  { | ||||
| -	return phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, | ||||
| +	u16 reg = MDIO_CTRL1; | ||||
| + | ||||
| +	if (genphy_c45_baset1_able(phydev)) | ||||
| +		reg = MDIO_AN_T1_CTRL; | ||||
| + | ||||
| +	return phy_set_bits_mmd(phydev, MDIO_MMD_AN, reg, | ||||
|  				MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg); | ||||
| @@ -210,11 +238,15 @@ EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg); | ||||
|   */ | ||||
|  int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart) | ||||
|  { | ||||
| -	int ret = 0; | ||||
| +	u16 reg = MDIO_CTRL1; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (genphy_c45_baset1_able(phydev)) | ||||
| +		reg = MDIO_AN_T1_CTRL; | ||||
|   | ||||
|  	if (!restart) { | ||||
|  		/* Configure and restart aneg if it wasn't set before */ | ||||
| -		ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); | ||||
| +		ret = phy_read_mmd(phydev, MDIO_MMD_AN, reg); | ||||
|  		if (ret < 0) | ||||
|  			return ret; | ||||
|   | ||||
| @@ -223,9 +255,9 @@ int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart) | ||||
|  	} | ||||
|   | ||||
|  	if (restart) | ||||
| -		ret = genphy_c45_restart_aneg(phydev); | ||||
| +		return genphy_c45_restart_aneg(phydev); | ||||
|   | ||||
| -	return ret; | ||||
| +	return 0; | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg); | ||||
|   | ||||
| @@ -242,7 +274,13 @@ EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg); | ||||
|   */ | ||||
|  int genphy_c45_aneg_done(struct phy_device *phydev) | ||||
|  { | ||||
| -	int val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); | ||||
| +	int reg = MDIO_STAT1; | ||||
| +	int val; | ||||
| + | ||||
| +	if (genphy_c45_baset1_able(phydev)) | ||||
| +		reg = MDIO_AN_T1_STAT; | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); | ||||
|   | ||||
|  	return val < 0 ? val : val & MDIO_AN_STAT1_COMPLETE ? 1 : 0; | ||||
|  } | ||||
| @@ -307,6 +345,49 @@ int genphy_c45_read_link(struct phy_device *phydev) | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(genphy_c45_read_link); | ||||
|   | ||||
| +/* Read the Clause 45 defined BASE-T1 AN (7.513) status register to check | ||||
| + * if autoneg is complete. If so read the BASE-T1 Autonegotiation | ||||
| + * Advertisement registers filling in the link partner advertisement, | ||||
| + * pause and asym_pause members in phydev. | ||||
| + */ | ||||
| +static int genphy_c45_baset1_read_lpa(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int val; | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_STAT); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	if (!(val & MDIO_AN_STAT1_COMPLETE)) { | ||||
| +		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->lp_advertising); | ||||
| +		mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, 0); | ||||
| +		mii_t1_adv_m_mod_linkmode_t(phydev->lp_advertising, 0); | ||||
| + | ||||
| +		phydev->pause = 0; | ||||
| +		phydev->asym_pause = 0; | ||||
| + | ||||
| +		return 0; | ||||
| +	} | ||||
| + | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->lp_advertising, 1); | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_L); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, val); | ||||
| +	phydev->pause = val & MDIO_AN_T1_ADV_L_PAUSE_CAP ? 1 : 0; | ||||
| +	phydev->asym_pause = val & MDIO_AN_T1_ADV_L_PAUSE_ASYM ? 1 : 0; | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_M); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	mii_t1_adv_m_mod_linkmode_t(phydev->lp_advertising, val); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  /** | ||||
|   * genphy_c45_read_lpa - read the link partner advertisement and pause | ||||
|   * @phydev: target phy_device struct | ||||
| @@ -321,6 +402,9 @@ int genphy_c45_read_lpa(struct phy_device *phydev) | ||||
|  { | ||||
|  	int val; | ||||
|   | ||||
| +	if (genphy_c45_baset1_able(phydev)) | ||||
| +		return genphy_c45_baset1_read_lpa(phydev); | ||||
| + | ||||
|  	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); | ||||
|  	if (val < 0) | ||||
|  		return val; | ||||
| @@ -436,6 +520,44 @@ int genphy_c45_read_mdix(struct phy_device *phydev) | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(genphy_c45_read_mdix); | ||||
|   | ||||
| +/** | ||||
| + * genphy_c45_pma_baset1_read_abilities - read supported baset1 link modes from PMA | ||||
| + * @phydev: target phy_device struct | ||||
| + * | ||||
| + * Read the supported link modes from the extended BASE-T1 ability register | ||||
| + */ | ||||
| +int genphy_c45_pma_baset1_read_abilities(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int val; | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, | ||||
| +			 phydev->supported, | ||||
| +			 val & MDIO_PMA_PMD_BT1_B10L_ABLE); | ||||
| + | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, | ||||
| +			 phydev->supported, | ||||
| +			 val & MDIO_PMA_PMD_BT1_B100_ABLE); | ||||
| + | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, | ||||
| +			 phydev->supported, | ||||
| +			 val & MDIO_PMA_PMD_BT1_B1000_ABLE); | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_STAT); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, | ||||
| +			 phydev->supported, | ||||
| +			 val & MDIO_AN_STAT1_ABLE); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_abilities); | ||||
| + | ||||
|  /** | ||||
|   * genphy_c45_pma_read_abilities - read supported link modes from PMA | ||||
|   * @phydev: target phy_device struct | ||||
| diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c | ||||
| index 99f265a..0325030 100644 | ||||
| --- a/drivers/net/phy/phy_device.c | ||||
| +++ b/drivers/net/phy/phy_device.c | ||||
| @@ -611,6 +612,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, | ||||
|   | ||||
|  	dev->autoneg = AUTONEG_ENABLE; | ||||
|   | ||||
| +	dev->pma_extable = -ENODATA; | ||||
|  	dev->is_c45 = is_c45; | ||||
|  	dev->phy_id = phy_id; | ||||
|  	if (c45_ids) | ||||
| diff --git a/include/linux/mdio.h b/include/linux/mdio.h | ||||
| index 006d1c1..6b14894 100644 | ||||
| --- a/include/linux/mdio.h | ||||
| +++ b/include/linux/mdio.h | ||||
| @@ -326,6 +326,42 @@ static inline void mii_10gbt_stat_mod_linkmode_lpa_t(unsigned long *advertising, | ||||
|  			 advertising, lpa & MDIO_AN_10GBT_STAT_LP10G); | ||||
|  } | ||||
|   | ||||
| +/** | ||||
| + * mii_t1_adv_l_mod_linkmode_t | ||||
| + * @advertising: target the linkmode advertisement settings | ||||
| + * @lpa: value of the BASE-T1 Autonegotiation Advertisement [15:0] Register | ||||
| + * | ||||
| + * A small helper function that translates BASE-T1 Autonegotiation | ||||
| + * Advertisement [15:0] Register bits to linkmode advertisement settings. | ||||
| + * Other bits in advertising aren't changed. | ||||
| + */ | ||||
| +static inline void mii_t1_adv_l_mod_linkmode_t(unsigned long *advertising, u32 lpa) | ||||
| +{ | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising, | ||||
| +			 lpa & MDIO_AN_T1_ADV_L_PAUSE_CAP); | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising, | ||||
| +			 lpa & MDIO_AN_T1_ADV_L_PAUSE_ASYM); | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * mii_t1_adv_m_mod_linkmode_t | ||||
| + * @advertising: target the linkmode advertisement settings | ||||
| + * @lpa: value of the BASE-T1 Autonegotiation Advertisement [31:16] Register | ||||
| + * | ||||
| + * A small helper function that translates BASE-T1 Autonegotiation | ||||
| + * Advertisement [31:16] Register bits to linkmode advertisement settings. | ||||
| + * Other bits in advertising aren't changed. | ||||
| + */ | ||||
| +static inline void mii_t1_adv_m_mod_linkmode_t(unsigned long *advertising, u32 lpa) | ||||
| +{ | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, | ||||
| +			 advertising, lpa & MDIO_AN_T1_ADV_M_B10L); | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, | ||||
| +			 advertising, lpa & MDIO_AN_T1_ADV_M_100BT1); | ||||
| +	linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, | ||||
| +			 advertising, lpa & MDIO_AN_T1_ADV_M_1000BT1); | ||||
| +} | ||||
| + | ||||
|  int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); | ||||
|  int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); | ||||
|   | ||||
| diff --git a/include/linux/phy.h b/include/linux/phy.h | ||||
| index fb21ddb..a9c2842 100644 | ||||
| --- a/include/linux/phy.h | ||||
| +++ b/include/linux/phy.h | ||||
| @@ -491,6 +493,8 @@ struct phy_device { | ||||
|  	u8 mdix; | ||||
|  	u8 mdix_ctrl; | ||||
|   | ||||
| +	int pma_extable; | ||||
| + | ||||
|  	void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier); | ||||
|  	void (*adjust_link)(struct net_device *dev); | ||||
|   | ||||
| diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h | ||||
| index 353a146..a47cadc 100644 | ||||
| --- a/include/uapi/linux/mdio.h | ||||
| +++ b/include/uapi/linux/mdio.h | ||||
| @@ -65,6 +65,17 @@ | ||||
|  #define MDIO_PCS_10GBRT_STAT2	33	/* 10GBASE-R/-T PCS status 2 */ | ||||
|  #define MDIO_AN_10GBT_CTRL	32	/* 10GBASE-T auto-negotiation control */ | ||||
|  #define MDIO_AN_10GBT_STAT	33	/* 10GBASE-T auto-negotiation status */ | ||||
| +#define MDIO_PMA_PMD_BT1	18	/* BASE-T1 PMA/PMD extended ability */ | ||||
| +#define MDIO_AN_T1_CTRL		512	/* BASE-T1 AN control */ | ||||
| +#define MDIO_AN_T1_STAT		513	/* BASE-T1 AN status */ | ||||
| +#define MDIO_AN_T1_ADV_L	514	/* BASE-T1 AN advertisement register [15:0] */ | ||||
| +#define MDIO_AN_T1_ADV_M	515	/* BASE-T1 AN advertisement register [31:16] */ | ||||
| +#define MDIO_AN_T1_ADV_H	516	/* BASE-T1 AN advertisement register [47:32] */ | ||||
| +#define MDIO_AN_T1_LP_L		517	/* BASE-T1 AN LP Base Page ability register [15:0] */ | ||||
| +#define MDIO_AN_T1_LP_M		518	/* BASE-T1 AN LP Base Page ability register [31:16] */ | ||||
| +#define MDIO_AN_T1_LP_H		519	/* BASE-T1 AN LP Base Page ability register [47:32] */ | ||||
| +#define MDIO_AN_10BT1_AN_CTRL	526	/* 10BASE-T1 AN control register */ | ||||
| +#define MDIO_AN_10BT1_AN_STAT	527	/* 10BASE-T1 AN status register */ | ||||
|   | ||||
|  /* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */ | ||||
|  #define MDIO_PMA_LASI_RXCTRL	0x9000	/* RX_ALARM control */ | ||||
| @@ -159,6 +170,7 @@ | ||||
|  #define MDIO_PMA_CTRL2_10BT		0x000f	/* 10BASE-T type */ | ||||
|  #define MDIO_PMA_CTRL2_2_5GBT		0x0030  /* 2.5GBaseT type */ | ||||
|  #define MDIO_PMA_CTRL2_5GBT		0x0031  /* 5GBaseT type */ | ||||
| +#define MDIO_PMA_CTRL2_BASET1		0x003D  /* BASE-T1 type */ | ||||
|  #define MDIO_PCS_CTRL2_TYPE		0x0003	/* PCS type selection */ | ||||
|  #define MDIO_PCS_CTRL2_10GBR		0x0000	/* 10GBASE-R type */ | ||||
|  #define MDIO_PCS_CTRL2_10GBX		0x0001	/* 10GBASE-X type */ | ||||
| @@ -212,6 +224,7 @@ | ||||
|  #define MDIO_PMA_EXTABLE_1000BKX	0x0040	/* 1000BASE-KX ability */ | ||||
|  #define MDIO_PMA_EXTABLE_100BTX		0x0080	/* 100BASE-TX ability */ | ||||
|  #define MDIO_PMA_EXTABLE_10BT		0x0100	/* 10BASE-T ability */ | ||||
| +#define MDIO_PMA_EXTABLE_BT1		0x0800	/* BASE-T1 ability */ | ||||
|  #define MDIO_PMA_EXTABLE_NBT		0x4000  /* 2.5/5GBASE-T ability */ | ||||
|   | ||||
|  /* PHY XGXS lane state register. */ | ||||
| @@ -264,6 +277,66 @@ | ||||
|  #define MDIO_AN_10GBT_STAT_MS		0x4000	/* Master/slave config */ | ||||
|  #define MDIO_AN_10GBT_STAT_MSFLT	0x8000	/* Master/slave config fault */ | ||||
|   | ||||
| +/* BASE-T1 PMA/PMD extended ability register. */ | ||||
| +#define MDIO_PMA_PMD_BT1_B100_ABLE	0x0001	/* 100BASE-T1 Ability */ | ||||
| +#define MDIO_PMA_PMD_BT1_B1000_ABLE	0x0002	/* 1000BASE-T1 Ability */ | ||||
| +#define MDIO_PMA_PMD_BT1_B10L_ABLE	0x0004	/* 10BASE-T1L Ability */ | ||||
| + | ||||
| +/* BASE-T1 auto-negotiation advertisement register [15:0] */ | ||||
| +#define MDIO_AN_T1_ADV_L_PAUSE_CAP	ADVERTISE_PAUSE_CAP | ||||
| +#define MDIO_AN_T1_ADV_L_PAUSE_ASYM	ADVERTISE_PAUSE_ASYM | ||||
| +#define MDIO_AN_T1_ADV_L_FORCE_MS	0x1000	/* Force Master/slave Configuration */ | ||||
| +#define MDIO_AN_T1_ADV_L_REMOTE_FAULT	ADVERTISE_RFAULT | ||||
| +#define MDIO_AN_T1_ADV_L_ACK		ADVERTISE_LPACK | ||||
| +#define MDIO_AN_T1_ADV_L_NEXT_PAGE_REQ	ADVERTISE_NPAGE | ||||
| + | ||||
| +/* BASE-T1 auto-negotiation advertisement register [31:16] */ | ||||
| +#define MDIO_AN_T1_ADV_M_B10L		0x4000	/* device is compatible with 10BASE-T1L */ | ||||
| +#define MDIO_AN_T1_ADV_M_1000BT1	0x0080	/* advertise 1000BASE-T1 */ | ||||
| +#define MDIO_AN_T1_ADV_M_100BT1		0x0020	/* advertise 100BASE-T1 */ | ||||
| +#define MDIO_AN_T1_ADV_M_MST		0x0010	/* advertise master preference */ | ||||
| + | ||||
| +/* BASE-T1 auto-negotiation advertisement register [47:32] */ | ||||
| +#define MDIO_AN_T1_ADV_H_10L_TX_HI_REQ	0x1000	/* 10BASE-T1L High Level Transmit Request */ | ||||
| +#define MDIO_AN_T1_ADV_H_10L_TX_HI	0x2000	/* 10BASE-T1L High Level Transmit Ability */ | ||||
| + | ||||
| +/* BASE-T1 AN LP Base Page ability register [15:0] */ | ||||
| +#define MDIO_AN_T1_LP_L_PAUSE_CAP	LPA_PAUSE_CAP | ||||
| +#define MDIO_AN_T1_LP_L_PAUSE_ASYM	LPA_PAUSE_ASYM | ||||
| +#define MDIO_AN_T1_LP_L_FORCE_MS	0x1000	/* LP Force Master/slave Configuration */ | ||||
| +#define MDIO_AN_T1_LP_L_REMOTE_FAULT	LPA_RFAULT | ||||
| +#define MDIO_AN_T1_LP_L_ACK		LPA_LPACK | ||||
| +#define MDIO_AN_T1_LP_L_NEXT_PAGE_REQ	LPA_NPAGE | ||||
| + | ||||
| +/* BASE-T1 AN LP Base Page ability register [31:16] */ | ||||
| +#define MDIO_AN_T1_LP_M_MST		0x0010	/* LP master preference */ | ||||
| +#define MDIO_AN_T1_LP_M_B10L		0x4000	/* LP is compatible with 10BASE-T1L */ | ||||
| + | ||||
| +/* BASE-T1 AN LP Base Page ability register [47:32] */ | ||||
| +#define MDIO_AN_T1_LP_H_10L_TX_HI_REQ	0x1000	/* 10BASE-T1L High Level LP Transmit Request */ | ||||
| +#define MDIO_AN_T1_LP_H_10L_TX_HI	0x2000	/* 10BASE-T1L High Level LP Transmit Ability */ | ||||
| + | ||||
| +/* 10BASE-T1 AN control register */ | ||||
| +#define MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L	0x4000 /* 10BASE-T1L EEE ability advertisement */ | ||||
| + | ||||
| +/* 10BASE-T1 AN status register */ | ||||
| +#define MDIO_AN_10BT1_AN_STAT_LPA_EEE_T1L	0x4000 /* 10BASE-T1L LP EEE ability advertisement */ | ||||
| + | ||||
| +/* BASE-T1 PMA/PMD control register */ | ||||
| +#define MDIO_PMA_PMD_BT1_CTRL_STRAP		0x000F /* Type selection (Strap) */ | ||||
| +#define MDIO_PMA_PMD_BT1_CTRL_STRAP_B1000	0x0001 /* Select 1000BASE-T1 */ | ||||
| +#define MDIO_PMA_PMD_BT1_CTRL_CFG_MST		0x4000 /* MASTER-SLAVE config value */ | ||||
| + | ||||
| +/* 1000BASE-T1 PCS control register */ | ||||
| +#define MDIO_PCS_1000BT1_CTRL_LOW_POWER		0x0800 /* Low power mode */ | ||||
| +#define MDIO_PCS_1000BT1_CTRL_DISABLE_TX	0x4000 /* Global PMA transmit disable */ | ||||
| +#define MDIO_PCS_1000BT1_CTRL_RESET		0x8000 /* Software reset value */ | ||||
| + | ||||
| +/* 1000BASE-T1 PCS status register */ | ||||
| +#define MDIO_PCS_1000BT1_STAT_LINK	0x0004 /* PCS Link is up */ | ||||
| +#define MDIO_PCS_1000BT1_STAT_FAULT	0x0080 /* There is a fault condition */ | ||||
| + | ||||
| + | ||||
|  /* EEE Supported/Advertisement/LP Advertisement registers. | ||||
|   * | ||||
|   * EEE capability Register (3.20), Advertisement (7.60) and | ||||
| @@ -0,0 +1,477 @@ | ||||
| diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig | ||||
| index e146955..291d5de 100644 | ||||
| --- a/drivers/net/phy/Kconfig | ||||
| +++ b/drivers/net/phy/Kconfig | ||||
| @@ -255,6 +255,13 @@ config LED_TRIGGER_PHY | ||||
|  		<Speed in megabits>Mbps OR <Speed in gigabits>Gbps OR link | ||||
|  		for any speed known to the PHY. | ||||
|   | ||||
| +config PHYLIB_LEDS | ||||
| +	def_bool OF | ||||
| +	depends on LEDS_CLASS=y || LEDS_CLASS=PHYLIB | ||||
| +	---help--- | ||||
| +	  When LED class support is enabled, phylib can automatically | ||||
| +	  probe LED setting from device tree. | ||||
| + | ||||
|   | ||||
|  comment "Switch configuration API + drivers" | ||||
|   | ||||
|  diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c | ||||
| index 99f265a..0325030 100644 | ||||
| --- a/drivers/net/phy/phy_device.c | ||||
| +++ b/drivers/net/phy/phy_device.c | ||||
| @@ -23,6 +23,7 @@ | ||||
|  #include <linux/mm.h> | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/mii.h> | ||||
| +#include <linux/of.h> | ||||
|  #include <linux/ethtool.h> | ||||
|  #include <linux/bitmap.h> | ||||
|  #include <linux/phy.h> | ||||
| @@ -621,6 +623,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, | ||||
|  	device_initialize(&mdiodev->dev); | ||||
|   | ||||
|  	dev->state = PHY_DOWN; | ||||
| +	INIT_LIST_HEAD(&dev->leds); | ||||
|   | ||||
|  	mutex_init(&dev->lock); | ||||
|  	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); | ||||
| @@ -2515,6 +2518,208 @@ static bool phy_drv_supports_irq(struct phy_driver *phydrv) | ||||
|  	return phydrv->config_intr && phydrv->ack_interrupt; | ||||
|  } | ||||
|   | ||||
| +static int phy_led_set_brightness(struct led_classdev *led_cdev, | ||||
| +				  enum led_brightness value) | ||||
| +{ | ||||
| +	struct phy_led *phyled = to_phy_led(led_cdev); | ||||
| +	struct phy_device *phydev = phyled->phydev; | ||||
| +	int err; | ||||
| + | ||||
| +	mutex_lock(&phydev->lock); | ||||
| +	err = phydev->drv->led_brightness_set(phydev, phyled->index, value); | ||||
| +	mutex_unlock(&phydev->lock); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int phy_led_blink_set(struct led_classdev *led_cdev, | ||||
| +			     unsigned long *delay_on, | ||||
| +			     unsigned long *delay_off) | ||||
| +{ | ||||
| +	struct phy_led *phyled = to_phy_led(led_cdev); | ||||
| +	struct phy_device *phydev = phyled->phydev; | ||||
| +	int err; | ||||
| + | ||||
| +	mutex_lock(&phydev->lock); | ||||
| +	err = phydev->drv->led_blink_set(phydev, phyled->index, | ||||
| +					 delay_on, delay_off); | ||||
| +	mutex_unlock(&phydev->lock); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static __maybe_unused struct device * | ||||
| +phy_led_hw_control_get_device(struct led_classdev *led_cdev) | ||||
| +{ | ||||
| +	struct phy_led *phyled = to_phy_led(led_cdev); | ||||
| +	struct phy_device *phydev = phyled->phydev; | ||||
| + | ||||
| +	if (phydev->attached_dev) | ||||
| +		return &phydev->attached_dev->dev; | ||||
| +	return NULL; | ||||
| +} | ||||
| + | ||||
| +static int __maybe_unused | ||||
| +phy_led_hw_control_get(struct led_classdev *led_cdev, | ||||
| +		       unsigned long *rules) | ||||
| +{ | ||||
| +	struct phy_led *phyled = to_phy_led(led_cdev); | ||||
| +	struct phy_device *phydev = phyled->phydev; | ||||
| +	int err; | ||||
| + | ||||
| +	mutex_lock(&phydev->lock); | ||||
| +	err = phydev->drv->led_hw_control_get(phydev, phyled->index, rules); | ||||
| +	mutex_unlock(&phydev->lock); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int __maybe_unused | ||||
| +phy_led_hw_control_set(struct led_classdev *led_cdev, | ||||
| +		       unsigned long rules) | ||||
| +{ | ||||
| +	struct phy_led *phyled = to_phy_led(led_cdev); | ||||
| +	struct phy_device *phydev = phyled->phydev; | ||||
| +	int err; | ||||
| + | ||||
| +	mutex_lock(&phydev->lock); | ||||
| +	err = phydev->drv->led_hw_control_set(phydev, phyled->index, rules); | ||||
| +	mutex_unlock(&phydev->lock); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static __maybe_unused int phy_led_hw_is_supported(struct led_classdev *led_cdev, | ||||
| +						  unsigned long rules) | ||||
| +{ | ||||
| +	struct phy_led *phyled = to_phy_led(led_cdev); | ||||
| +	struct phy_device *phydev = phyled->phydev; | ||||
| +	int err; | ||||
| + | ||||
| +	mutex_lock(&phydev->lock); | ||||
| +	err = phydev->drv->led_hw_is_supported(phydev, phyled->index, rules); | ||||
| +	mutex_unlock(&phydev->lock); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static void phy_leds_unregister(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct phy_led *phyled, *tmp; | ||||
| + | ||||
| +	list_for_each_entry_safe(phyled, tmp, &phydev->leds, list) { | ||||
| +		led_classdev_unregister(&phyled->led_cdev); | ||||
| +		list_del(&phyled->list); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static int of_phy_led(struct phy_device *phydev, | ||||
| +		      struct device_node *led) | ||||
| +{ | ||||
| +	struct device *dev = &phydev->mdio.dev; | ||||
| +	struct led_init_data init_data = {}; | ||||
| +	struct led_classdev *cdev; | ||||
| +	unsigned long modes = 0; | ||||
| +	struct phy_led *phyled; | ||||
| +	u32 index; | ||||
| +	int err; | ||||
| + | ||||
| +	phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL); | ||||
| +	if (!phyled) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	cdev = &phyled->led_cdev; | ||||
| +	phyled->phydev = phydev; | ||||
| + | ||||
| +	err = of_property_read_u32(led, "reg", &index); | ||||
| +	if (err) | ||||
| +		return err; | ||||
| +	if (index > U8_MAX) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	if (of_property_read_bool(led, "active-high")) | ||||
| +		set_bit(PHY_LED_ACTIVE_HIGH, &modes); | ||||
| +	if (of_property_read_bool(led, "active-low")) | ||||
| +		set_bit(PHY_LED_ACTIVE_LOW, &modes); | ||||
| +	if (of_property_read_bool(led, "inactive-high-impedance")) | ||||
| +		set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes); | ||||
| + | ||||
| +	if (WARN_ON(modes & BIT(PHY_LED_ACTIVE_LOW) && | ||||
| +		    modes & BIT(PHY_LED_ACTIVE_HIGH))) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	if (modes) { | ||||
| +		/* Return error if asked to set polarity modes but not supported */ | ||||
| +		if (!phydev->drv->led_polarity_set) | ||||
| +			return -EINVAL; | ||||
| + | ||||
| +		err = phydev->drv->led_polarity_set(phydev, index, modes); | ||||
| +		if (err) | ||||
| +			return err; | ||||
| +	} | ||||
| + | ||||
| +	phyled->index = index; | ||||
| +	if (phydev->drv->led_brightness_set) | ||||
| +		cdev->brightness_set_blocking = phy_led_set_brightness; | ||||
| +	if (phydev->drv->led_blink_set) | ||||
| +		cdev->blink_set = phy_led_blink_set; | ||||
| + | ||||
| +#ifdef CONFIG_LEDS_TRIGGERS | ||||
| +	if (phydev->drv->led_hw_is_supported && | ||||
| +	    phydev->drv->led_hw_control_set && | ||||
| +	    phydev->drv->led_hw_control_get) { | ||||
| +		cdev->hw_control_is_supported = phy_led_hw_is_supported; | ||||
| +		cdev->hw_control_set = phy_led_hw_control_set; | ||||
| +		cdev->hw_control_get = phy_led_hw_control_get; | ||||
| +		cdev->hw_control_trigger = "netdev"; | ||||
| +	} | ||||
| + | ||||
| +	cdev->hw_control_get_device = phy_led_hw_control_get_device; | ||||
| +#endif | ||||
| +	cdev->max_brightness = 1; | ||||
| +	init_data.devicename = dev_name(&phydev->mdio.dev); | ||||
| +	init_data.fwnode = of_fwnode_handle(led); | ||||
| +	init_data.devname_mandatory = true; | ||||
| + | ||||
| +	err = led_classdev_register_ext(dev, cdev, &init_data); | ||||
| +	if (err) | ||||
| +		return err; | ||||
| + | ||||
| +	list_add(&phyled->list, &phydev->leds); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int of_phy_leds(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct device_node *node = phydev->mdio.dev.of_node; | ||||
| +	struct device_node *leds, *led; | ||||
| +	int err; | ||||
| + | ||||
| +	if (!IS_ENABLED(CONFIG_OF_MDIO)) | ||||
| +		return 0; | ||||
| + | ||||
| +	if (!node) | ||||
| +		return 0; | ||||
| + | ||||
| +	leds = of_get_child_by_name(node, "leds"); | ||||
| +	if (!leds) | ||||
| +		return 0; | ||||
| + | ||||
| +	for_each_available_child_of_node(leds, led) { | ||||
| +		err = of_phy_led(phydev, led); | ||||
| +		if (err) { | ||||
| +			of_node_put(led); | ||||
| +			of_node_put(leds); | ||||
| +			phy_leds_unregister(phydev); | ||||
| +			return err; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	of_node_put(leds); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  /** | ||||
|   * phy_probe - probe and init a PHY device | ||||
|   * @dev: device to probe and init | ||||
| @@ -2613,6 +2818,12 @@ static int phy_probe(struct device *dev) | ||||
|  	/* Set the state to READY by default */ | ||||
|  	phydev->state = PHY_READY; | ||||
|   | ||||
| +	/* Get the LEDs from the device tree, and instantiate standard | ||||
| +	 * LEDs for them. | ||||
| +	 */ | ||||
| +	if (IS_ENABLED(CONFIG_PHYLIB_LEDS)) | ||||
| +		err = of_phy_leds(phydev); | ||||
| + | ||||
|  out: | ||||
|  	mutex_unlock(&phydev->lock); | ||||
|   | ||||
| @@ -2625,6 +2836,9 @@ static int phy_remove(struct device *dev) | ||||
|   | ||||
|  	cancel_delayed_work_sync(&phydev->state_queue); | ||||
|   | ||||
| +	if (IS_ENABLED(CONFIG_PHYLIB_LEDS)) | ||||
| +		phy_leds_unregister(phydev); | ||||
| + | ||||
|  	mutex_lock(&phydev->lock); | ||||
|  	phydev->state = PHY_DOWN; | ||||
|  	mutex_unlock(&phydev->lock); | ||||
| diff --git a/include/linux/leds.h b/include/linux/leds.h | ||||
| index efb309d..d5ef91a 100644 | ||||
| --- a/include/linux/leds.h | ||||
| +++ b/include/linux/leds.h | ||||
| @@ -139,6 +139,49 @@ struct led_classdev { | ||||
|  	void			*trigger_data; | ||||
|  	/* true if activated - deactivate routine uses it to do cleanup */ | ||||
|  	bool			activated; | ||||
| + | ||||
| +	/* Unique trigger name supported by LED set in hw control mode */ | ||||
| +	const char		*hw_control_trigger; | ||||
| +	/* | ||||
| +	 * Check if the LED driver supports the requested mode provided by the | ||||
| +	 * defined supported trigger to setup the LED to hw control mode. | ||||
| +	 * | ||||
| +	 * Return 0 on success. Return -EOPNOTSUPP when the passed flags are not | ||||
| +	 * supported and software fallback needs to be used. | ||||
| +	 * Return a negative error number on any other case  for check fail due | ||||
| +	 * to various reason like device not ready or timeouts. | ||||
| +	 */ | ||||
| +	int			(*hw_control_is_supported)(struct led_classdev *led_cdev, | ||||
| +							   unsigned long flags); | ||||
| +	/* | ||||
| +	 * Activate hardware control, LED driver will use the provided flags | ||||
| +	 * from the supported trigger and setup the LED to be driven by hardware | ||||
| +	 * following the requested mode from the trigger flags. | ||||
| +	 * Deactivate hardware blink control by setting brightness to LED_OFF via | ||||
| +	 * the brightness_set() callback. | ||||
| +	 * | ||||
| +	 * Return 0 on success, a negative error number on flags apply fail. | ||||
| +	 */ | ||||
| +	int			(*hw_control_set)(struct led_classdev *led_cdev, | ||||
| +						  unsigned long flags); | ||||
| +	/* | ||||
| +	 * Get from the LED driver the current mode that the LED is set in hw | ||||
| +	 * control mode and put them in flags. | ||||
| +	 * Trigger can use this to get the initial state of a LED already set in | ||||
| +	 * hardware blink control. | ||||
| +	 * | ||||
| +	 * Return 0 on success, a negative error number on failing parsing the | ||||
| +	 * initial mode. Error from this function is NOT FATAL as the device | ||||
| +	 * may be in a not supported initial state by the attached LED trigger. | ||||
| +	 */ | ||||
| +	int			(*hw_control_get)(struct led_classdev *led_cdev, | ||||
| +						  unsigned long *flags); | ||||
| +	/* | ||||
| +	 * Get the device this LED blinks in response to. | ||||
| +	 * e.g. for a PHY LED, it is the network device. If the LED is | ||||
| +	 * not yet associated to a device, return NULL. | ||||
| +	 */ | ||||
| +	struct device		*(*hw_control_get_device)(struct led_classdev *led_cdev); | ||||
|  #endif | ||||
|   | ||||
|  #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED | ||||
| @@ -455,6 +498,24 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev) | ||||
|   | ||||
|  #endif /* CONFIG_LEDS_TRIGGERS */ | ||||
|   | ||||
| +/* Trigger specific enum */ | ||||
| +enum led_trigger_netdev_modes { | ||||
| +	TRIGGER_NETDEV_LINK = 0, | ||||
| +	TRIGGER_NETDEV_LINK_10, | ||||
| +	TRIGGER_NETDEV_LINK_100, | ||||
| +	TRIGGER_NETDEV_LINK_1000, | ||||
| +	TRIGGER_NETDEV_LINK_2500, | ||||
| +	TRIGGER_NETDEV_LINK_5000, | ||||
| +	TRIGGER_NETDEV_LINK_10000, | ||||
| +	TRIGGER_NETDEV_HALF_DUPLEX, | ||||
| +	TRIGGER_NETDEV_FULL_DUPLEX, | ||||
| +	TRIGGER_NETDEV_TX, | ||||
| +	TRIGGER_NETDEV_RX, | ||||
| + | ||||
| +	/* Keep last */ | ||||
| +	__TRIGGER_NETDEV_MAX, | ||||
| +}; | ||||
| + | ||||
|  /* Trigger specific functions */ | ||||
|  #ifdef CONFIG_LEDS_TRIGGER_DISK | ||||
|  extern void ledtrig_disk_activity(bool write); | ||||
| diff --git a/include/linux/phy.h b/include/linux/phy.h | ||||
| index fb21ddb..a9c2842 100644 | ||||
| --- a/include/linux/phy.h | ||||
| +++ b/include/linux/phy.h | ||||
| @@ -14,6 +14,7 @@ | ||||
|  #include <linux/compiler.h> | ||||
|  #include <linux/spinlock.h> | ||||
|  #include <linux/ethtool.h> | ||||
| +#include <linux/leds.h> | ||||
|  #include <linux/linkmode.h> | ||||
|  #include <linux/mdio.h> | ||||
|  #include <linux/mii.h> | ||||
| @@ -462,6 +463,7 @@ struct phy_device { | ||||
|   | ||||
|  	struct phy_led_trigger *led_link_trigger; | ||||
|  #endif | ||||
| +	struct list_head leds; | ||||
|   | ||||
|  	/* | ||||
|  	 * Interrupt number for this PHY | ||||
| @@ -502,6 +506,33 @@ struct phy_device { | ||||
|  #define to_phy_device(d) container_of(to_mdio_device(d), \ | ||||
|  				      struct phy_device, mdio) | ||||
|   | ||||
| +/** | ||||
| + * struct phy_led: An LED driven by the PHY | ||||
| + * | ||||
| + * @list: List of LEDs | ||||
| + * @phydev: PHY this LED is attached to | ||||
| + * @led_cdev: Standard LED class structure | ||||
| + * @index: Number of the LED | ||||
| + */ | ||||
| +struct phy_led { | ||||
| +	struct list_head list; | ||||
| +	struct phy_device *phydev; | ||||
| +	struct led_classdev led_cdev; | ||||
| +	u8 index; | ||||
| +}; | ||||
| + | ||||
| +#define to_phy_led(d) container_of(d, struct phy_led, led_cdev) | ||||
| + | ||||
| +/* Modes for PHY LED configuration */ | ||||
| +enum phy_led_modes { | ||||
| +	PHY_LED_ACTIVE_HIGH = 0, | ||||
| +	PHY_LED_ACTIVE_LOW = 1, | ||||
| +	PHY_LED_INACTIVE_HIGH_IMPEDANCE = 2, | ||||
| + | ||||
| +	/* keep it last */ | ||||
| +	__PHY_LED_MODES_NUM, | ||||
| +}; | ||||
| + | ||||
|  /* struct phy_driver: Driver structure for a particular PHY type | ||||
|   * | ||||
|   * driver_data: static driver data | ||||
| @@ -705,6 +736,73 @@ struct phy_driver { | ||||
|  			    struct ethtool_tunable *tuna, | ||||
|  			    const void *data); | ||||
|  	int (*set_loopback)(struct phy_device *dev, bool enable); | ||||
| + | ||||
| +	/** | ||||
| +	 * @led_brightness_set: Set a PHY LED brightness. Index | ||||
| +	 * indicates which of the PHYs led should be set. Value | ||||
| +	 * follows the standard LED class meaning, e.g. LED_OFF, | ||||
| +	 * LED_HALF, LED_FULL. | ||||
| +	 */ | ||||
| +	int (*led_brightness_set)(struct phy_device *dev, | ||||
| +				  u8 index, enum led_brightness value); | ||||
| + | ||||
| +	/** | ||||
| +	 * @led_blink_set: Set a PHY LED blinking.  Index indicates | ||||
| +	 * which of the PHYs led should be configured to blink. Delays | ||||
| +	 * are in milliseconds and if both are zero then a sensible | ||||
| +	 * default should be chosen.  The call should adjust the | ||||
| +	 * timings in that case and if it can't match the values | ||||
| +	 * specified exactly. | ||||
| +	 */ | ||||
| +	int (*led_blink_set)(struct phy_device *dev, u8 index, | ||||
| +			     unsigned long *delay_on, | ||||
| +			     unsigned long *delay_off); | ||||
| +	/** | ||||
| +	 * @led_hw_is_supported: Can the HW support the given rules. | ||||
| +	 * @dev: PHY device which has the LED | ||||
| +	 * @index: Which LED of the PHY device | ||||
| +	 * @rules The core is interested in these rules | ||||
| +	 * | ||||
| +	 * Return 0 if yes,  -EOPNOTSUPP if not, or an error code. | ||||
| +	 */ | ||||
| +	int (*led_hw_is_supported)(struct phy_device *dev, u8 index, | ||||
| +				   unsigned long rules); | ||||
| +	/** | ||||
| +	 * @led_hw_control_set: Set the HW to control the LED | ||||
| +	 * @dev: PHY device which has the LED | ||||
| +	 * @index: Which LED of the PHY device | ||||
| +	 * @rules The rules used to control the LED | ||||
| +	 * | ||||
| +	 * Returns 0, or a an error code. | ||||
| +	 */ | ||||
| +	int (*led_hw_control_set)(struct phy_device *dev, u8 index, | ||||
| +				  unsigned long rules); | ||||
| +	/** | ||||
| +	 * @led_hw_control_get: Get how the HW is controlling the LED | ||||
| +	 * @dev: PHY device which has the LED | ||||
| +	 * @index: Which LED of the PHY device | ||||
| +	 * @rules Pointer to the rules used to control the LED | ||||
| +	 * | ||||
| +	 * Set *@rules to how the HW is currently blinking. Returns 0 | ||||
| +	 * on success, or a error code if the current blinking cannot | ||||
| +	 * be represented in rules, or some other error happens. | ||||
| +	 */ | ||||
| +	int (*led_hw_control_get)(struct phy_device *dev, u8 index, | ||||
| +				  unsigned long *rules); | ||||
| + | ||||
| +	/** | ||||
| +	 * @led_polarity_set: Set the LED polarity modes | ||||
| +	 * @dev: PHY device which has the LED | ||||
| +	 * @index: Which LED of the PHY device | ||||
| +	 * @modes: bitmap of LED polarity modes | ||||
| +	 * | ||||
| +	 * Configure LED with all the required polarity modes in @modes | ||||
| +	 * to make it correctly turn ON or OFF. | ||||
| +	 * | ||||
| +	 * Returns 0, or an error code. | ||||
| +	 */ | ||||
| +	int (*led_polarity_set)(struct phy_device *dev, int index, | ||||
| +				unsigned long modes); | ||||
|  }; | ||||
|  #define to_phy_driver(d) container_of(to_mdio_common_driver(d),		\ | ||||
|  				      struct phy_driver, mdiodrv) | ||||
| @@ -0,0 +1,654 @@ | ||||
| From f2f8228373e6aef6ece42b2da2273d983af581a7 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Fri, 16 May 2025 16:12:49 +0800 | ||||
| Subject: [PATCH] leds: trigger: netdev: add additional specific link speed and | ||||
|  duplex mode | ||||
|  | ||||
| --- | ||||
|  drivers/leds/trigger/ledtrig-netdev.c | 421 +++++++++++++++++++------- | ||||
|  1 file changed, 308 insertions(+), 113 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c | ||||
| index f4d670e..7b56ae3 100644 | ||||
| --- a/drivers/leds/trigger/ledtrig-netdev.c | ||||
| +++ b/drivers/leds/trigger/ledtrig-netdev.c | ||||
| @@ -20,10 +20,13 @@ | ||||
|  #include <linux/list.h> | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/netdevice.h> | ||||
| -#include <linux/spinlock.h> | ||||
| +#include <linux/mutex.h> | ||||
| +#include <linux/rtnetlink.h> | ||||
|  #include <linux/timer.h> | ||||
|  #include "../leds.h" | ||||
|   | ||||
| +#define NETDEV_LED_DEFAULT_INTERVAL	50 | ||||
| + | ||||
|  /* | ||||
|   * Configurable sysfs attributes: | ||||
|   * | ||||
| @@ -37,7 +40,7 @@ | ||||
|   */ | ||||
|   | ||||
|  struct led_netdev_data { | ||||
| -	spinlock_t lock; | ||||
| +	struct mutex lock; | ||||
|   | ||||
|  	struct delayed_work work; | ||||
|  	struct notifier_block notifier; | ||||
| @@ -50,16 +53,11 @@ struct led_netdev_data { | ||||
|  	unsigned int last_activity; | ||||
|   | ||||
|  	unsigned long mode; | ||||
| -#define NETDEV_LED_LINK	0 | ||||
| -#define NETDEV_LED_TX	1 | ||||
| -#define NETDEV_LED_RX	2 | ||||
| -#define NETDEV_LED_MODE_LINKUP	3 | ||||
| -}; | ||||
| +	int link_speed; | ||||
| +	u8 duplex; | ||||
|   | ||||
| -enum netdev_led_attr { | ||||
| -	NETDEV_ATTR_LINK, | ||||
| -	NETDEV_ATTR_TX, | ||||
| -	NETDEV_ATTR_RX | ||||
| +	bool carrier_link_up; | ||||
| +	bool hw_control; | ||||
|  }; | ||||
|   | ||||
|  static void set_baseline_state(struct led_netdev_data *trigger_data) | ||||
| @@ -67,16 +65,60 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) | ||||
|  	int current_brightness; | ||||
|  	struct led_classdev *led_cdev = trigger_data->led_cdev; | ||||
|   | ||||
| +	/* Already validated, hw control is possible with the requested mode */ | ||||
| +	if (trigger_data->hw_control) { | ||||
| +		led_cdev->hw_control_set(led_cdev, trigger_data->mode); | ||||
| + | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
|  	current_brightness = led_cdev->brightness; | ||||
|  	if (current_brightness) | ||||
|  		led_cdev->blink_brightness = current_brightness; | ||||
|  	if (!led_cdev->blink_brightness) | ||||
|  		led_cdev->blink_brightness = led_cdev->max_brightness; | ||||
|   | ||||
| -	if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode)) | ||||
| +	if (!trigger_data->carrier_link_up) { | ||||
|  		led_set_brightness(led_cdev, LED_OFF); | ||||
| -	else { | ||||
| -		if (test_bit(NETDEV_LED_LINK, &trigger_data->mode)) | ||||
| +	} else { | ||||
| +		bool blink_on = false; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode)) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) && | ||||
| +		    trigger_data->link_speed == SPEED_10) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) && | ||||
| +		    trigger_data->link_speed == SPEED_100) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) && | ||||
| +		    trigger_data->link_speed == SPEED_1000) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) && | ||||
| +		    trigger_data->link_speed == SPEED_2500) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) && | ||||
| +		    trigger_data->link_speed == SPEED_5000) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) && | ||||
| +		    trigger_data->link_speed == SPEED_10000) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && | ||||
| +		    trigger_data->duplex == DUPLEX_HALF) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) && | ||||
| +		    trigger_data->duplex == DUPLEX_FULL) | ||||
| +			blink_on = true; | ||||
| + | ||||
| +		if (blink_on) | ||||
|  			led_set_brightness(led_cdev, | ||||
|  					   led_cdev->blink_brightness); | ||||
|  		else | ||||
| @@ -85,44 +127,129 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) | ||||
|  		/* If we are looking for RX/TX start periodically | ||||
|  		 * checking stats | ||||
|  		 */ | ||||
| -		if (test_bit(NETDEV_LED_TX, &trigger_data->mode) || | ||||
| -		    test_bit(NETDEV_LED_RX, &trigger_data->mode)) | ||||
| +		if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) || | ||||
| +		    test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) | ||||
|  			schedule_delayed_work(&trigger_data->work, 0); | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +static bool supports_hw_control(struct led_classdev *led_cdev) | ||||
| +{ | ||||
| +	if (!led_cdev->hw_control_get || !led_cdev->hw_control_set || | ||||
| +	    !led_cdev->hw_control_is_supported) | ||||
| +		return false; | ||||
| + | ||||
| +	return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name); | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * Validate the configured netdev is the same as the one associated with | ||||
| + * the LED driver in hw control. | ||||
| + */ | ||||
| +static bool validate_net_dev(struct led_classdev *led_cdev, | ||||
| +			     struct net_device *net_dev) | ||||
| +{ | ||||
| +	struct device *dev = led_cdev->hw_control_get_device(led_cdev); | ||||
| +	struct net_device *ndev; | ||||
| + | ||||
| +	if (!dev) | ||||
| +		return false; | ||||
| + | ||||
| +	ndev = to_net_dev(dev); | ||||
| + | ||||
| +	return ndev == net_dev; | ||||
| +} | ||||
| + | ||||
| +static bool can_hw_control(struct led_netdev_data *trigger_data) | ||||
| +{ | ||||
| +	unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL); | ||||
| +	unsigned int interval = atomic_read(&trigger_data->interval); | ||||
| +	struct led_classdev *led_cdev = trigger_data->led_cdev; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (!supports_hw_control(led_cdev)) | ||||
| +		return false; | ||||
| + | ||||
| +	/* | ||||
| +	 * Interval must be set to the default | ||||
| +	 * value. Any different value is rejected if in hw | ||||
| +	 * control. | ||||
| +	 */ | ||||
| +	if (interval != default_interval) | ||||
| +		return false; | ||||
| + | ||||
| +	/* | ||||
| +	 * net_dev must be set with hw control, otherwise no | ||||
| +	 * blinking can be happening and there is nothing to | ||||
| +	 * offloaded. Additionally, for hw control to be | ||||
| +	 * valid, the configured netdev must be the same as | ||||
| +	 * netdev associated to the LED. | ||||
| +	 */ | ||||
| +	if (!validate_net_dev(led_cdev, trigger_data->net_dev)) | ||||
| +		return false; | ||||
| + | ||||
| +	/* Check if the requested mode is supported */ | ||||
| +	ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode); | ||||
| +	/* Fall back to software blinking if not supported */ | ||||
| +	if (ret == -EOPNOTSUPP) | ||||
| +		return false; | ||||
| +	if (ret) { | ||||
| +		dev_warn(led_cdev->dev, | ||||
| +			 "Current mode check failed with error %d\n", ret); | ||||
| +		return false; | ||||
| +	} | ||||
| + | ||||
| +	return true; | ||||
| +} | ||||
| + | ||||
| +static void get_device_state(struct led_netdev_data *trigger_data) | ||||
| +{ | ||||
| +	struct ethtool_link_ksettings cmd; | ||||
| + | ||||
| +	trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev); | ||||
| +	if (!trigger_data->carrier_link_up) | ||||
| +		return; | ||||
| + | ||||
| +	if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) { | ||||
| +		trigger_data->link_speed = cmd.base.speed; | ||||
| +		trigger_data->duplex = cmd.base.duplex; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  static ssize_t device_name_show(struct device *dev, | ||||
|  				struct device_attribute *attr, char *buf) | ||||
|  { | ||||
|  	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); | ||||
|  	ssize_t len; | ||||
|   | ||||
| -	spin_lock_bh(&trigger_data->lock); | ||||
| +	mutex_lock(&trigger_data->lock); | ||||
|  	len = sprintf(buf, "%s\n", trigger_data->device_name); | ||||
| -	spin_unlock_bh(&trigger_data->lock); | ||||
| +	mutex_unlock(&trigger_data->lock); | ||||
|   | ||||
|  	return len; | ||||
|  } | ||||
|   | ||||
| -static ssize_t device_name_store(struct device *dev, | ||||
| -				 struct device_attribute *attr, const char *buf, | ||||
| -				 size_t size) | ||||
| +static int set_device_name(struct led_netdev_data *trigger_data, | ||||
| +			   const char *name, size_t size) | ||||
|  { | ||||
| -	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); | ||||
| - | ||||
|  	if (size >= IFNAMSIZ) | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	cancel_delayed_work_sync(&trigger_data->work); | ||||
|   | ||||
| -	spin_lock_bh(&trigger_data->lock); | ||||
| +	/* | ||||
| +	 * Take RTNL lock before trigger_data lock to prevent potential | ||||
| +	 * deadlock with netdev notifier registration. | ||||
| +	 */ | ||||
| +	rtnl_lock(); | ||||
| +	mutex_lock(&trigger_data->lock); | ||||
|   | ||||
|  	if (trigger_data->net_dev) { | ||||
|  		dev_put(trigger_data->net_dev); | ||||
|  		trigger_data->net_dev = NULL; | ||||
|  	} | ||||
|   | ||||
| -	memcpy(trigger_data->device_name, buf, size); | ||||
| +	memcpy(trigger_data->device_name, name, size); | ||||
|  	trigger_data->device_name[size] = 0; | ||||
|  	if (size > 0 && trigger_data->device_name[size - 1] == '\n') | ||||
|  		trigger_data->device_name[size - 1] = 0; | ||||
| @@ -131,36 +258,56 @@ static ssize_t device_name_store(struct device *dev, | ||||
|  		trigger_data->net_dev = | ||||
|  		    dev_get_by_name(&init_net, trigger_data->device_name); | ||||
|   | ||||
| -	clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); | ||||
| -	if (trigger_data->net_dev != NULL) | ||||
| -		if (netif_carrier_ok(trigger_data->net_dev)) | ||||
| -			set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); | ||||
| +	trigger_data->carrier_link_up = false; | ||||
| +	trigger_data->link_speed = SPEED_UNKNOWN; | ||||
| +	trigger_data->duplex = DUPLEX_UNKNOWN; | ||||
| +	if (trigger_data->net_dev) | ||||
| +		get_device_state(trigger_data); | ||||
|   | ||||
|  	trigger_data->last_activity = 0; | ||||
|   | ||||
|  	set_baseline_state(trigger_data); | ||||
| -	spin_unlock_bh(&trigger_data->lock); | ||||
| +	mutex_unlock(&trigger_data->lock); | ||||
| +	rtnl_unlock(); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static ssize_t device_name_store(struct device *dev, | ||||
| +				 struct device_attribute *attr, const char *buf, | ||||
| +				 size_t size) | ||||
| +{ | ||||
| +	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = set_device_name(trigger_data, buf, size); | ||||
|   | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
|  	return size; | ||||
|  } | ||||
|   | ||||
|  static DEVICE_ATTR_RW(device_name); | ||||
|   | ||||
|  static ssize_t netdev_led_attr_show(struct device *dev, char *buf, | ||||
| -	enum netdev_led_attr attr) | ||||
| +				    enum led_trigger_netdev_modes attr) | ||||
|  { | ||||
|  	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); | ||||
|  	int bit; | ||||
|   | ||||
|  	switch (attr) { | ||||
| -	case NETDEV_ATTR_LINK: | ||||
| -		bit = NETDEV_LED_LINK; | ||||
| -		break; | ||||
| -	case NETDEV_ATTR_TX: | ||||
| -		bit = NETDEV_LED_TX; | ||||
| -		break; | ||||
| -	case NETDEV_ATTR_RX: | ||||
| -		bit = NETDEV_LED_RX; | ||||
| +	case TRIGGER_NETDEV_LINK: | ||||
| +	case TRIGGER_NETDEV_LINK_10: | ||||
| +	case TRIGGER_NETDEV_LINK_100: | ||||
| +	case TRIGGER_NETDEV_LINK_1000: | ||||
| +	case TRIGGER_NETDEV_LINK_2500: | ||||
| +	case TRIGGER_NETDEV_LINK_5000: | ||||
| +	case TRIGGER_NETDEV_LINK_10000: | ||||
| +	case TRIGGER_NETDEV_HALF_DUPLEX: | ||||
| +	case TRIGGER_NETDEV_FULL_DUPLEX: | ||||
| +	case TRIGGER_NETDEV_TX: | ||||
| +	case TRIGGER_NETDEV_RX: | ||||
| +		bit = attr; | ||||
|  		break; | ||||
|  	default: | ||||
|  		return -EINVAL; | ||||
| @@ -170,10 +317,10 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf, | ||||
|  } | ||||
|   | ||||
|  static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, | ||||
| -	size_t size, enum netdev_led_attr attr) | ||||
| +				     size_t size, enum led_trigger_netdev_modes attr) | ||||
|  { | ||||
|  	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); | ||||
| -	unsigned long state; | ||||
| +	unsigned long state, mode = trigger_data->mode; | ||||
|  	int ret; | ||||
|  	int bit; | ||||
|   | ||||
| @@ -182,72 +329,71 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, | ||||
|  		return ret; | ||||
|   | ||||
|  	switch (attr) { | ||||
| -	case NETDEV_ATTR_LINK: | ||||
| -		bit = NETDEV_LED_LINK; | ||||
| -		break; | ||||
| -	case NETDEV_ATTR_TX: | ||||
| -		bit = NETDEV_LED_TX; | ||||
| -		break; | ||||
| -	case NETDEV_ATTR_RX: | ||||
| -		bit = NETDEV_LED_RX; | ||||
| +	case TRIGGER_NETDEV_LINK: | ||||
| +	case TRIGGER_NETDEV_LINK_10: | ||||
| +	case TRIGGER_NETDEV_LINK_100: | ||||
| +	case TRIGGER_NETDEV_LINK_1000: | ||||
| +	case TRIGGER_NETDEV_LINK_2500: | ||||
| +	case TRIGGER_NETDEV_LINK_5000: | ||||
| +	case TRIGGER_NETDEV_LINK_10000: | ||||
| +	case TRIGGER_NETDEV_HALF_DUPLEX: | ||||
| +	case TRIGGER_NETDEV_FULL_DUPLEX: | ||||
| +	case TRIGGER_NETDEV_TX: | ||||
| +	case TRIGGER_NETDEV_RX: | ||||
| +		bit = attr; | ||||
|  		break; | ||||
|  	default: | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| -	cancel_delayed_work_sync(&trigger_data->work); | ||||
| - | ||||
|  	if (state) | ||||
| -		set_bit(bit, &trigger_data->mode); | ||||
| +		set_bit(bit, &mode); | ||||
|  	else | ||||
| -		clear_bit(bit, &trigger_data->mode); | ||||
| - | ||||
| -	set_baseline_state(trigger_data); | ||||
| - | ||||
| -	return size; | ||||
| -} | ||||
| - | ||||
| -static ssize_t link_show(struct device *dev, | ||||
| -	struct device_attribute *attr, char *buf) | ||||
| -{ | ||||
| -	return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK); | ||||
| -} | ||||
| - | ||||
| -static ssize_t link_store(struct device *dev, | ||||
| -	struct device_attribute *attr, const char *buf, size_t size) | ||||
| -{ | ||||
| -	return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK); | ||||
| -} | ||||
| - | ||||
| -static DEVICE_ATTR_RW(link); | ||||
| - | ||||
| -static ssize_t tx_show(struct device *dev, | ||||
| -	struct device_attribute *attr, char *buf) | ||||
| -{ | ||||
| -	return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX); | ||||
| -} | ||||
| +		clear_bit(bit, &mode); | ||||
| + | ||||
| +	if (test_bit(TRIGGER_NETDEV_LINK, &mode) && | ||||
| +	    (test_bit(TRIGGER_NETDEV_LINK_10, &mode) || | ||||
| +	     test_bit(TRIGGER_NETDEV_LINK_100, &mode) || | ||||
| +	     test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || | ||||
| +	     test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || | ||||
| +	     test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || | ||||
| +	     test_bit(TRIGGER_NETDEV_LINK_10000, &mode))) | ||||
| +		return -EINVAL; | ||||
|   | ||||
| -static ssize_t tx_store(struct device *dev, | ||||
| -	struct device_attribute *attr, const char *buf, size_t size) | ||||
| -{ | ||||
| -	return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX); | ||||
| -} | ||||
| +	cancel_delayed_work_sync(&trigger_data->work); | ||||
|   | ||||
| -static DEVICE_ATTR_RW(tx); | ||||
| +	trigger_data->mode = mode; | ||||
| +	trigger_data->hw_control = can_hw_control(trigger_data); | ||||
|   | ||||
| -static ssize_t rx_show(struct device *dev, | ||||
| -	struct device_attribute *attr, char *buf) | ||||
| -{ | ||||
| -	return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX); | ||||
| -} | ||||
| +	set_baseline_state(trigger_data); | ||||
|   | ||||
| -static ssize_t rx_store(struct device *dev, | ||||
| -	struct device_attribute *attr, const char *buf, size_t size) | ||||
| -{ | ||||
| -	return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX); | ||||
| +	return size; | ||||
|  } | ||||
|   | ||||
| -static DEVICE_ATTR_RW(rx); | ||||
| +#define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \ | ||||
| +	static ssize_t trigger_name##_show(struct device *dev, \ | ||||
| +		struct device_attribute *attr, char *buf) \ | ||||
| +	{ \ | ||||
| +		return netdev_led_attr_show(dev, buf, trigger); \ | ||||
| +	} \ | ||||
| +	static ssize_t trigger_name##_store(struct device *dev, \ | ||||
| +		struct device_attribute *attr, const char *buf, size_t size) \ | ||||
| +	{ \ | ||||
| +		return netdev_led_attr_store(dev, buf, size, trigger); \ | ||||
| +	} \ | ||||
| +	static DEVICE_ATTR_RW(trigger_name) | ||||
| + | ||||
| +DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK); | ||||
| +DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10); | ||||
| +DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100); | ||||
| +DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000); | ||||
| +DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500); | ||||
| +DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000); | ||||
| +DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000); | ||||
| +DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); | ||||
| +DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); | ||||
| +DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); | ||||
| +DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX); | ||||
|   | ||||
|  static ssize_t interval_show(struct device *dev, | ||||
|  			     struct device_attribute *attr, char *buf) | ||||
| @@ -266,6 +412,9 @@ static ssize_t interval_store(struct device *dev, | ||||
|  	unsigned long value; | ||||
|  	int ret; | ||||
|   | ||||
| +	if (trigger_data->hw_control) | ||||
| +		return -EINVAL; | ||||
| + | ||||
|  	ret = kstrtoul(buf, 0, &value); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
| @@ -283,12 +432,31 @@ static ssize_t interval_store(struct device *dev, | ||||
|   | ||||
|  static DEVICE_ATTR_RW(interval); | ||||
|   | ||||
| +static ssize_t offloaded_show(struct device *dev, | ||||
| +			      struct device_attribute *attr, char *buf) | ||||
| +{ | ||||
| +	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); | ||||
| + | ||||
| +	return sprintf(buf, "%d\n", trigger_data->hw_control); | ||||
| +} | ||||
| + | ||||
| +static DEVICE_ATTR_RO(offloaded); | ||||
| + | ||||
|  static struct attribute *netdev_trig_attrs[] = { | ||||
|  	&dev_attr_device_name.attr, | ||||
|  	&dev_attr_link.attr, | ||||
| +	&dev_attr_link_10.attr, | ||||
| +	&dev_attr_link_100.attr, | ||||
| +	&dev_attr_link_1000.attr, | ||||
| +	&dev_attr_link_2500.attr, | ||||
| +	&dev_attr_link_5000.attr, | ||||
| +	&dev_attr_link_10000.attr, | ||||
| +	&dev_attr_full_duplex.attr, | ||||
| +	&dev_attr_half_duplex.attr, | ||||
|  	&dev_attr_rx.attr, | ||||
|  	&dev_attr_tx.attr, | ||||
|  	&dev_attr_interval.attr, | ||||
| +	&dev_attr_offloaded.attr, | ||||
|  	NULL | ||||
|  }; | ||||
|  ATTRIBUTE_GROUPS(netdev_trig); | ||||
| @@ -313,19 +481,19 @@ static int netdev_trig_notify(struct notifier_block *nb, | ||||
|   | ||||
|  	cancel_delayed_work_sync(&trigger_data->work); | ||||
|   | ||||
| -	spin_lock_bh(&trigger_data->lock); | ||||
| +	mutex_lock(&trigger_data->lock); | ||||
|   | ||||
| -	clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); | ||||
| +	trigger_data->carrier_link_up = false; | ||||
| +	trigger_data->link_speed = SPEED_UNKNOWN; | ||||
| +	trigger_data->duplex = DUPLEX_UNKNOWN; | ||||
|  	switch (evt) { | ||||
|  	case NETDEV_CHANGENAME: | ||||
| -		if (netif_carrier_ok(dev)) | ||||
| -			set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); | ||||
| -		fallthrough; | ||||
|  	case NETDEV_REGISTER: | ||||
| -		if (trigger_data->net_dev) | ||||
| -			dev_put(trigger_data->net_dev); | ||||
| +		dev_put(trigger_data->net_dev); | ||||
|  		dev_hold(dev); | ||||
|  		trigger_data->net_dev = dev; | ||||
| +		if (evt == NETDEV_CHANGENAME) | ||||
| +			get_device_state(trigger_data); | ||||
|  		break; | ||||
|  	case NETDEV_UNREGISTER: | ||||
|  		dev_put(trigger_data->net_dev); | ||||
| @@ -333,14 +501,13 @@ static int netdev_trig_notify(struct notifier_block *nb, | ||||
|  		break; | ||||
|  	case NETDEV_UP: | ||||
|  	case NETDEV_CHANGE: | ||||
| -		if (netif_carrier_ok(dev)) | ||||
| -			set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); | ||||
| +		get_device_state(trigger_data); | ||||
|  		break; | ||||
|  	} | ||||
|   | ||||
|  	set_baseline_state(trigger_data); | ||||
|   | ||||
| -	spin_unlock_bh(&trigger_data->lock); | ||||
| +	mutex_unlock(&trigger_data->lock); | ||||
|   | ||||
|  	return NOTIFY_DONE; | ||||
|  } | ||||
| @@ -363,21 +530,29 @@ static void netdev_trig_work(struct work_struct *work) | ||||
|  	} | ||||
|   | ||||
|  	/* If we are not looking for RX/TX then return  */ | ||||
| -	if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) && | ||||
| -	    !test_bit(NETDEV_LED_RX, &trigger_data->mode)) | ||||
| +	if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) && | ||||
| +	    !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) | ||||
|  		return; | ||||
|   | ||||
|  	dev_stats = dev_get_stats(trigger_data->net_dev, &temp); | ||||
|  	new_activity = | ||||
| -	    (test_bit(NETDEV_LED_TX, &trigger_data->mode) ? | ||||
| +	    (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ? | ||||
|  		dev_stats->tx_packets : 0) + | ||||
| -	    (test_bit(NETDEV_LED_RX, &trigger_data->mode) ? | ||||
| +	    (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ? | ||||
|  		dev_stats->rx_packets : 0); | ||||
|   | ||||
|  	if (trigger_data->last_activity != new_activity) { | ||||
|  		led_stop_software_blink(trigger_data->led_cdev); | ||||
|   | ||||
| -		invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode); | ||||
| +		invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) || | ||||
| +			 test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode); | ||||
|  		interval = jiffies_to_msecs( | ||||
|  				atomic_read(&trigger_data->interval)); | ||||
|  		/* base state is ON (link present) */ | ||||
| @@ -395,13 +570,15 @@ static void netdev_trig_work(struct work_struct *work) | ||||
|  static int netdev_trig_activate(struct led_classdev *led_cdev) | ||||
|  { | ||||
|  	struct led_netdev_data *trigger_data; | ||||
| +	unsigned long mode = 0; | ||||
| +	struct device *dev; | ||||
|  	int rc; | ||||
|   | ||||
|  	trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); | ||||
|  	if (!trigger_data) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| -	spin_lock_init(&trigger_data->lock); | ||||
| +	mutex_init(&trigger_data->lock); | ||||
|   | ||||
|  	trigger_data->notifier.notifier_call = netdev_trig_notify; | ||||
|  	trigger_data->notifier.priority = 10; | ||||
| @@ -413,9 +590,26 @@ static int netdev_trig_activate(struct led_classdev *led_cdev) | ||||
|  	trigger_data->device_name[0] = 0; | ||||
|   | ||||
|  	trigger_data->mode = 0; | ||||
| -	atomic_set(&trigger_data->interval, msecs_to_jiffies(50)); | ||||
| +	atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL)); | ||||
|  	trigger_data->last_activity = 0; | ||||
|   | ||||
| +	/* Check if hw control is active by default on the LED. | ||||
| +	 * Init already enabled mode in hw control. | ||||
| +	 */ | ||||
| +	if (supports_hw_control(led_cdev)) { | ||||
| +		dev = led_cdev->hw_control_get_device(led_cdev); | ||||
| +		if (dev) { | ||||
| +			const char *name = dev_name(dev); | ||||
| + | ||||
| +			set_device_name(trigger_data, name, strlen(name)); | ||||
| +			trigger_data->hw_control = true; | ||||
| + | ||||
| +			rc = led_cdev->hw_control_get(led_cdev, &mode); | ||||
| +			if (!rc) | ||||
| +				trigger_data->mode = mode; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
|  	led_set_trigger_data(led_cdev, trigger_data); | ||||
|   | ||||
|  	rc = register_netdevice_notifier(&trigger_data->notifier); | ||||
| @@ -433,8 +627,9 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev) | ||||
|   | ||||
|  	cancel_delayed_work_sync(&trigger_data->work); | ||||
|   | ||||
| -	if (trigger_data->net_dev) | ||||
| -		dev_put(trigger_data->net_dev); | ||||
| +	led_set_brightness(led_cdev, LED_OFF); | ||||
| + | ||||
| +	dev_put(trigger_data->net_dev); | ||||
|   | ||||
|  	kfree(trigger_data); | ||||
|  } | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -109,109 +109,82 @@ | ||||
|  					return err; | ||||
| --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c | ||||
| +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c | ||||
| @@ -506,6 +506,404 @@ int mtk_pinconf_bias_get_rev1(struct mtk | ||||
|  	return 0; | ||||
| @@ -6,7 +6,6 @@ | ||||
|   * | ||||
|   */ | ||||
|   | ||||
| -#include <dt-bindings/pinctrl/mt65xx.h> | ||||
|  #include <linux/device.h> | ||||
|  #include <linux/err.h> | ||||
|  #include <linux/gpio/driver.h> | ||||
| @@ -67,44 +66,34 @@ static int mtk_hw_pin_field_lookup(struc | ||||
|  				   const struct mtk_pin_desc *desc, | ||||
|  				   int field, struct mtk_pin_field *pfd) | ||||
|  { | ||||
| -	const struct mtk_pin_field_calc *c; | ||||
| +	const struct mtk_pin_field_calc *c, *e; | ||||
|  	const struct mtk_pin_reg_calc *rc; | ||||
| -	int start = 0, end, check; | ||||
| -	bool found = false; | ||||
|  	u32 bits; | ||||
|   | ||||
|  	if (hw->soc->reg_cal && hw->soc->reg_cal[field].range) { | ||||
|  		rc = &hw->soc->reg_cal[field]; | ||||
|  	} else { | ||||
|  		dev_dbg(hw->dev, | ||||
| -			"Not support field %d for this soc\n", field); | ||||
| +			"Not support field %d for pin %d (%s)\n", | ||||
| +			field, desc->number, desc->name); | ||||
|  		return -ENOTSUPP; | ||||
|  	} | ||||
|   | ||||
| -	end = rc->nranges - 1; | ||||
| +	c = rc->range; | ||||
| +	e = c + rc->nranges; | ||||
|   | ||||
| -	while (start <= end) { | ||||
| -		check = (start + end) >> 1; | ||||
| -		if (desc->number >= rc->range[check].s_pin | ||||
| -		 && desc->number <= rc->range[check].e_pin) { | ||||
| -			found = true; | ||||
| -			break; | ||||
| -		} else if (start == end) | ||||
| +	while (c < e) { | ||||
| +		if (desc->number >= c->s_pin && desc->number <= c->e_pin) | ||||
|  			break; | ||||
| -		else if (desc->number < rc->range[check].s_pin) | ||||
| -			end = check - 1; | ||||
| -		else | ||||
| -			start = check + 1; | ||||
| +		c++; | ||||
|  	} | ||||
|   | ||||
| -	if (!found) { | ||||
| +	if (c >= e) { | ||||
|  		dev_dbg(hw->dev, "Not support field %d for pin = %d (%s)\n", | ||||
|  			field, desc->number, desc->name); | ||||
|  		return -ENOTSUPP; | ||||
|  	} | ||||
|   | ||||
| -	c = rc->range + check; | ||||
| - | ||||
|  	if (c->i_base > hw->nbase - 1) { | ||||
|  		dev_err(hw->dev, | ||||
|  			"Invalid base for field %d for pin = %d (%s)\n", | ||||
| @@ -193,9 +182,6 @@ int mtk_hw_set_value(struct mtk_pinctrl | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| -	if (value < 0 || value > pf.mask) | ||||
| -		return -EINVAL; | ||||
| - | ||||
|  	if (!pf.next) | ||||
|  		mtk_rmw(hw, pf.index, pf.offset, pf.mask << pf.bitpos, | ||||
|  			(value & pf.mask) << pf.bitpos); | ||||
| @@ -619,6 +605,186 @@ out: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +/* Combo for the following pull register type: | ||||
| + * 1. PU + PD | ||||
| + * 2. PULLSEL + PULLEN | ||||
| + * 3. PUPD + R0 + R1 | ||||
| + */ | ||||
| +static int mtk_pinconf_bias_set_pu_pd(struct mtk_pinctrl *hw, | ||||
| +				const struct mtk_pin_desc *desc, | ||||
| +				u32 pullup, u32 arg) | ||||
| +{ | ||||
| +	int err, pu, pd; | ||||
| + | ||||
| +	if (arg == MTK_DISABLE) { | ||||
| +		pu = 0; | ||||
| +		pd = 0; | ||||
| +	} else if ((arg == MTK_ENABLE) && pullup) { | ||||
| +		pu = 1; | ||||
| +		pd = 0; | ||||
| +	} else if ((arg == MTK_ENABLE) && !pullup) { | ||||
| +		pu = 0; | ||||
| +		pd = 1; | ||||
| +	} else { | ||||
| +		err = -EINVAL; | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PU, pu); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PD, pd); | ||||
| + | ||||
| +out: | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int mtk_pinconf_bias_set_pullsel_pullen(struct mtk_pinctrl *hw, | ||||
| +				const struct mtk_pin_desc *desc, | ||||
| +				u32 pullup, u32 arg) | ||||
| +{ | ||||
| +	int err, enable; | ||||
| + | ||||
| +	if (arg == MTK_DISABLE) | ||||
| +		enable = 0; | ||||
| +	else if (arg == MTK_ENABLE) | ||||
| +		enable = 1; | ||||
| +	else { | ||||
| +		err = -EINVAL; | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PULLEN, enable); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PULLSEL, pullup); | ||||
| + | ||||
| +out: | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int mtk_pinconf_bias_set_pupd_r1_r0(struct mtk_pinctrl *hw, | ||||
| +				const struct mtk_pin_desc *desc, | ||||
| +				u32 pullup, u32 arg) | ||||
| +{ | ||||
| +	int err, r0, r1; | ||||
| + | ||||
| +	if ((arg == MTK_DISABLE) || (arg == MTK_PUPD_SET_R1R0_00)) { | ||||
| +		pullup = 0; | ||||
| +		r0 = 0; | ||||
| +		r1 = 0; | ||||
| +	} else if (arg == MTK_PUPD_SET_R1R0_01) { | ||||
| +		r0 = 1; | ||||
| +		r1 = 0; | ||||
| +	} else if (arg == MTK_PUPD_SET_R1R0_10) { | ||||
| +		r0 = 0; | ||||
| +		r1 = 1; | ||||
| +	} else if (arg == MTK_PUPD_SET_R1R0_11) { | ||||
| +		r0 = 1; | ||||
| +		r1 = 1; | ||||
| +	} else { | ||||
| +		err = -EINVAL; | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	/* MTK HW PUPD bit: 1 for pull-down, 0 for pull-up */ | ||||
| +	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PUPD, !pullup); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_R0, r0); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_R1, r1); | ||||
| + | ||||
| +out: | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int mtk_hw_pin_rsel_lookup(struct mtk_pinctrl *hw, | ||||
| +				  const struct mtk_pin_desc *desc, | ||||
| +				  u32 pullup, u32 arg, u32 *rsel_val) | ||||
| @@ -392,105 +365,59 @@ | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int mtk_pinconf_bias_get_pu_pd(struct mtk_pinctrl *hw, | ||||
| +				const struct mtk_pin_desc *desc, | ||||
| +				u32 *pullup, u32 *enable) | ||||
| +{ | ||||
| +	int err, pu, pd; | ||||
| + | ||||
| +	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PU, &pu); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PD, &pd); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	if (pu == 0 && pd == 0) { | ||||
| +		*pullup = 0; | ||||
| +		*enable = MTK_DISABLE; | ||||
| +	} else if (pu == 1 && pd == 0) { | ||||
| +		*pullup = 1; | ||||
| +		*enable = MTK_ENABLE; | ||||
| +	} else if (pu == 0 && pd == 1) { | ||||
| +		*pullup = 0; | ||||
| +		*enable = MTK_ENABLE; | ||||
| +	} else | ||||
| +		err = -EINVAL; | ||||
| + | ||||
| +out: | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int mtk_pinconf_bias_get_pullsel_pullen(struct mtk_pinctrl *hw, | ||||
| +				const struct mtk_pin_desc *desc, | ||||
| +				u32 *pullup, u32 *enable) | ||||
| +{ | ||||
| +	int err; | ||||
| + | ||||
| +	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PULLSEL, pullup); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PULLEN, enable); | ||||
| + | ||||
| +out: | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int mtk_pinconf_bias_get_pupd_r1_r0(struct mtk_pinctrl *hw, | ||||
| +				const struct mtk_pin_desc *desc, | ||||
| +				u32 *pullup, u32 *enable) | ||||
| +{ | ||||
| +	int err, r0, r1; | ||||
| + | ||||
| +	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PUPD, pullup); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| +	/* MTK HW PUPD bit: 1 for pull-down, 0 for pull-up */ | ||||
| +	*pullup = !(*pullup); | ||||
| + | ||||
| +	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_R0, &r0); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_R1, &r1); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	if ((r1 == 0) && (r0 == 0)) | ||||
| +		*enable = MTK_PUPD_SET_R1R0_00; | ||||
| +	else if ((r1 == 0) && (r0 == 1)) | ||||
| +		*enable = MTK_PUPD_SET_R1R0_01; | ||||
| +	else if ((r1 == 1) && (r0 == 0)) | ||||
| +		*enable = MTK_PUPD_SET_R1R0_10; | ||||
| +	else if ((r1 == 1) && (r0 == 1)) | ||||
| +		*enable = MTK_PUPD_SET_R1R0_11; | ||||
| +	else | ||||
| +		err = -EINVAL; | ||||
| + | ||||
| +out: | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +int mtk_pinconf_bias_get_combo(struct mtk_pinctrl *hw, | ||||
| +			      const struct mtk_pin_desc *desc, | ||||
| +			      u32 *pullup, u32 *enable) | ||||
| +{ | ||||
|  static int mtk_pinconf_bias_get_pu_pd(struct mtk_pinctrl *hw, | ||||
|  				const struct mtk_pin_desc *desc, | ||||
|  				u32 *pullup, u32 *enable) | ||||
| @@ -700,45 +866,43 @@ out: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -int mtk_pinconf_bias_set_combo(struct mtk_pinctrl *hw, | ||||
| -				const struct mtk_pin_desc *desc, | ||||
| -				u32 pullup, u32 arg) | ||||
| -{ | ||||
| -	int err; | ||||
| - | ||||
| -	err = mtk_pinconf_bias_set_pu_pd(hw, desc, pullup, arg); | ||||
| -	if (!err) | ||||
| -		goto out; | ||||
| - | ||||
| -	err = mtk_pinconf_bias_set_pullsel_pullen(hw, desc, pullup, arg); | ||||
| -	if (!err) | ||||
| -		goto out; | ||||
| - | ||||
| -	err = mtk_pinconf_bias_set_pupd_r1_r0(hw, desc, pullup, arg); | ||||
| - | ||||
| -out: | ||||
| -	return err; | ||||
| -} | ||||
| - | ||||
|  int mtk_pinconf_bias_get_combo(struct mtk_pinctrl *hw, | ||||
|  			      const struct mtk_pin_desc *desc, | ||||
|  			      u32 *pullup, u32 *enable) | ||||
|  { | ||||
| -	int err; | ||||
| +	int err = -ENOTSUPP; | ||||
| +	u32 try_all_type; | ||||
| + | ||||
|   | ||||
| -	err = mtk_pinconf_bias_get_pu_pd(hw, desc, pullup, enable); | ||||
| -	if (!err) | ||||
| -		goto out; | ||||
| +	if (hw->soc->pull_type) | ||||
| +		try_all_type = hw->soc->pull_type[desc->number]; | ||||
| +	else | ||||
| +		try_all_type = MTK_PULL_TYPE_MASK; | ||||
| + | ||||
|   | ||||
| -	err = mtk_pinconf_bias_get_pullsel_pullen(hw, desc, pullup, enable); | ||||
| -	if (!err) | ||||
| -		goto out; | ||||
| +	if (try_all_type & MTK_PULL_RSEL_TYPE) { | ||||
| +		err = mtk_pinconf_bias_get_rsel(hw, desc, pullup, enable); | ||||
| +		if (!err) | ||||
| +			return err; | ||||
| +	} | ||||
| + | ||||
|   | ||||
| -	err = mtk_pinconf_bias_get_pupd_r1_r0(hw, desc, pullup, enable); | ||||
| +	if (try_all_type & MTK_PULL_PU_PD_TYPE) { | ||||
| +		err = mtk_pinconf_bias_get_pu_pd(hw, desc, pullup, enable); | ||||
| +		if (!err) | ||||
| @@ -506,14 +433,44 @@ | ||||
| + | ||||
| +	if (try_all_type & MTK_PULL_PUPD_R1R0_TYPE) | ||||
| +		err = mtk_pinconf_bias_get_pupd_r1_r0(hw, desc, pullup, enable); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
|   | ||||
| -out: | ||||
|  	return err; | ||||
|  } | ||||
| +EXPORT_SYMBOL_GPL(mtk_pinconf_bias_get_combo); | ||||
| + | ||||
|   | ||||
|  /* Revision 0 */ | ||||
|  int mtk_pinconf_drive_set(struct mtk_pinctrl *hw, | ||||
|  			  const struct mtk_pin_desc *desc, u32 arg) | ||||
| @@ -831,18 +995,6 @@ int mtk_pinconf_drive_get_rev1(struct mt | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -int mtk_pinconf_drive_set_raw(struct mtk_pinctrl *hw, | ||||
| -			       const struct mtk_pin_desc *desc, u32 arg) | ||||
| -{ | ||||
| -	return mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_DRV, arg); | ||||
| -} | ||||
| - | ||||
| -int mtk_pinconf_drive_get_raw(struct mtk_pinctrl *hw, | ||||
| -			       const struct mtk_pin_desc *desc, int *val) | ||||
| -{ | ||||
| -	return mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_DRV, val); | ||||
| -} | ||||
| - | ||||
|  int mtk_pinconf_adv_pull_set(struct mtk_pinctrl *hw, | ||||
|  			     const struct mtk_pin_desc *desc, bool pullup, | ||||
|  			     u32 arg) | ||||
| @@ -876,9 +1028,7 @@ int mtk_pinconf_adv_pull_set(struct mtk_ | ||||
|  			if (err) | ||||
|  				return err; | ||||
|  		} else { | ||||
| -			err = mtk_pinconf_bias_set_rev1(hw, desc, pullup); | ||||
| -			if (err) | ||||
| -				err = mtk_pinconf_bias_set(hw, desc, pullup); | ||||
| +			return -ENOTSUPP; | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h | ||||
| +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h | ||||
| @@ -17,6 +17,34 @@ | ||||
| @@ -593,19 +550,22 @@ | ||||
|   | ||||
|  	/* Specific pinconfig operations */ | ||||
|  	int (*bias_disable_set)(struct mtk_pinctrl *hw, | ||||
| @@ -215,7 +264,10 @@ struct mtk_pin_soc { | ||||
| @@ -215,12 +264,10 @@ struct mtk_pin_soc { | ||||
|  			const struct mtk_pin_desc *desc, bool pullup); | ||||
|  	int (*bias_get)(struct mtk_pinctrl *hw, | ||||
|  			const struct mtk_pin_desc *desc, bool pullup, int *res); | ||||
| - | ||||
| +	int (*bias_set_combo)(struct mtk_pinctrl *hw, | ||||
|  	int (*bias_set_combo)(struct mtk_pinctrl *hw, | ||||
| -			const struct mtk_pin_desc *desc, u32 pullup, u32 arg); | ||||
| +			      const struct mtk_pin_desc *desc, u32 pullup, u32 arg); | ||||
| +	int (*bias_get_combo)(struct mtk_pinctrl *hw, | ||||
|  	int (*bias_get_combo)(struct mtk_pinctrl *hw, | ||||
| -			const struct mtk_pin_desc *desc, u32 *pullup, u32 *arg); | ||||
| - | ||||
| +			      const struct mtk_pin_desc *desc, u32 *pullup, u32 *arg); | ||||
|  	int (*drive_set)(struct mtk_pinctrl *hw, | ||||
|  			 const struct mtk_pin_desc *desc, u32 arg); | ||||
|  	int (*drive_get)(struct mtk_pinctrl *hw, | ||||
| @@ -246,6 +298,10 @@ struct mtk_pinctrl { | ||||
| @@ -251,6 +298,10 @@ struct mtk_pinctrl { | ||||
|  	struct mtk_eint			*eint; | ||||
|  	struct mtk_pinctrl_group	*groups; | ||||
|  	const char          **grp_names; | ||||
| @@ -616,7 +576,18 @@ | ||||
|  }; | ||||
|   | ||||
|  void mtk_rmw(struct mtk_pinctrl *pctl, u8 i, u32 reg, u32 mask, u32 set); | ||||
| @@ -282,7 +338,12 @@ int mtk_pinconf_drive_set(struct mtk_pin | ||||
| @@ -282,28 +333,22 @@ int mtk_pinconf_bias_set_rev1(struct mtk | ||||
|  int mtk_pinconf_bias_get_rev1(struct mtk_pinctrl *hw, | ||||
|  			      const struct mtk_pin_desc *desc, bool pullup, | ||||
|  			      int *res); | ||||
| -int mtk_pinconf_bias_set_combo(struct mtk_pinctrl *hw, | ||||
| -				const struct mtk_pin_desc *desc, | ||||
| -				u32 pullup, u32 enable); | ||||
| -int mtk_pinconf_bias_get_combo(struct mtk_pinctrl *hw, | ||||
| -			      const struct mtk_pin_desc *desc, | ||||
| -			      u32 *pullup, u32 *enable); | ||||
|   | ||||
|  int mtk_pinconf_drive_set(struct mtk_pinctrl *hw, | ||||
|  			  const struct mtk_pin_desc *desc, u32 arg); | ||||
|  int mtk_pinconf_drive_get(struct mtk_pinctrl *hw, | ||||
|  			  const struct mtk_pin_desc *desc, int *val); | ||||
| @@ -630,3 +601,13 @@ | ||||
|  int mtk_pinconf_drive_set_rev1(struct mtk_pinctrl *hw, | ||||
|  			       const struct mtk_pin_desc *desc, u32 arg); | ||||
|  int mtk_pinconf_drive_get_rev1(struct mtk_pinctrl *hw, | ||||
|  			       const struct mtk_pin_desc *desc, int *val); | ||||
|   | ||||
| -int mtk_pinconf_drive_set_raw(struct mtk_pinctrl *hw, | ||||
| -			       const struct mtk_pin_desc *desc, u32 arg); | ||||
| -int mtk_pinconf_drive_get_raw(struct mtk_pinctrl *hw, | ||||
| -			       const struct mtk_pin_desc *desc, int *val); | ||||
| - | ||||
|  int mtk_pinconf_adv_pull_set(struct mtk_pinctrl *hw, | ||||
|  			     const struct mtk_pin_desc *desc, bool pullup, | ||||
|  			     u32 arg); | ||||
|   | ||||
| @@ -0,0 +1,347 @@ | ||||
| --- a/drivers/soc/mediatek/Kconfig | ||||
| +++ b/drivers/soc/mediatek/Kconfig | ||||
| @@ -17,6 +17,15 @@ config MTK_CMDQ | ||||
|  	  time limitation, such as updating display configuration during the | ||||
|  	  vblank. | ||||
|   | ||||
| +config MTK_DEVAPC | ||||
| +	tristate "Mediatek Device APC Support" | ||||
| +	help | ||||
| +	  Say yes here to enable support for Mediatek Device APC driver. | ||||
| +	  This driver is mainly used to handle the violation which catches | ||||
| +	  unexpected transaction. | ||||
| +	  The violation information is logged for further analysis or | ||||
| +	  countermeasures. | ||||
| + | ||||
|  config MTK_INFRACFG | ||||
|  	bool "MediaTek INFRACFG Support" | ||||
|  	select REGMAP | ||||
| --- a/drivers/soc/mediatek/Makefile | ||||
| +++ b/drivers/soc/mediatek/Makefile | ||||
| @@ -1,5 +1,6 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0-only | ||||
|  obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o | ||||
| +obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o | ||||
|  obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o | ||||
|  obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o | ||||
|  obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/soc/mediatek/mtk-devapc.c | ||||
| @@ -0,0 +1,317 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0 | ||||
| +/* | ||||
| + * Copyright (C) 2020 MediaTek Inc. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/interrupt.h> | ||||
| +#include <linux/iopoll.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/of_irq.h> | ||||
| +#include <linux/of_address.h> | ||||
| + | ||||
| +#define VIO_MOD_TO_REG_IND(m)	((m) / 32) | ||||
| +#define VIO_MOD_TO_REG_OFF(m)	((m) % 32) | ||||
| + | ||||
| +struct mtk_devapc_vio_dbgs { | ||||
| +	union { | ||||
| +		u32 vio_dbg0; | ||||
| +		struct { | ||||
| +			u32 mstid:16; | ||||
| +			u32 dmnid:6; | ||||
| +			u32 vio_w:1; | ||||
| +			u32 vio_r:1; | ||||
| +			u32 addr_h:4; | ||||
| +			u32 resv:4; | ||||
| +		} dbg0_bits; | ||||
| +	}; | ||||
| + | ||||
| +	u32 vio_dbg1; | ||||
| +}; | ||||
| + | ||||
| +struct mtk_devapc_regs_ofs { | ||||
| +	/* reg offset */ | ||||
| +	u32 vio_mask_offset; | ||||
| +	u32 vio_sta_offset; | ||||
| +	u32 vio_dbg0_offset; | ||||
| +	u32 vio_dbg1_offset; | ||||
| +	u32 apc_con_offset; | ||||
| +	u32 vio_shift_sta_offset; | ||||
| +	u32 vio_shift_sel_offset; | ||||
| +	u32 vio_shift_con_offset; | ||||
| +}; | ||||
| + | ||||
| +struct mtk_devapc_data { | ||||
| +	/* numbers of violation index */ | ||||
| +	u32 vio_idx_num; | ||||
| +	const struct mtk_devapc_regs_ofs *regs_ofs; | ||||
| +}; | ||||
| + | ||||
| +struct mtk_devapc_context { | ||||
| +	struct device *dev; | ||||
| +	void __iomem *infra_base; | ||||
| +	struct clk *infra_clk; | ||||
| +	const struct mtk_devapc_data *data; | ||||
| +}; | ||||
| + | ||||
| +static void clear_vio_status(struct mtk_devapc_context *ctx) | ||||
| +{ | ||||
| +	void __iomem *reg; | ||||
| +	int i; | ||||
| + | ||||
| +	reg = ctx->infra_base + ctx->data->regs_ofs->vio_sta_offset; | ||||
| + | ||||
| +	for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++) | ||||
| +		writel(GENMASK(31, 0), reg + 4 * i); | ||||
| + | ||||
| +	writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1, 0), | ||||
| +	       reg + 4 * i); | ||||
| +} | ||||
| + | ||||
| +static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask) | ||||
| +{ | ||||
| +	void __iomem *reg; | ||||
| +	u32 val; | ||||
| +	int i; | ||||
| + | ||||
| +	reg = ctx->infra_base + ctx->data->regs_ofs->vio_mask_offset; | ||||
| + | ||||
| +	if (mask) | ||||
| +		val = GENMASK(31, 0); | ||||
| +	else | ||||
| +		val = 0; | ||||
| + | ||||
| +	for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++) | ||||
| +		writel(val, reg + 4 * i); | ||||
| + | ||||
| +	val = readl(reg + 4 * i); | ||||
| +	if (mask) | ||||
| +		val |= GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1, | ||||
| +			       0); | ||||
| +	else | ||||
| +		val &= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1, | ||||
| +				0); | ||||
| + | ||||
| +	writel(val, reg + 4 * i); | ||||
| +} | ||||
| + | ||||
| +#define PHY_DEVAPC_TIMEOUT	0x10000 | ||||
| + | ||||
| +/* | ||||
| + * devapc_sync_vio_dbg - do "shift" mechansim" to get full violation information. | ||||
| + *                       shift mechanism is depends on devapc hardware design. | ||||
| + *                       Mediatek devapc set multiple slaves as a group. | ||||
| + *                       When violation is triggered, violation info is kept | ||||
| + *                       inside devapc hardware. | ||||
| + *                       Driver should do shift mechansim to sync full violation | ||||
| + *                       info to VIO_DBGs registers. | ||||
| + * | ||||
| + */ | ||||
| +static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx) | ||||
| +{ | ||||
| +	void __iomem *pd_vio_shift_sta_reg; | ||||
| +	void __iomem *pd_vio_shift_sel_reg; | ||||
| +	void __iomem *pd_vio_shift_con_reg; | ||||
| +	int min_shift_group; | ||||
| +	int ret; | ||||
| +	u32 val; | ||||
| + | ||||
| +	pd_vio_shift_sta_reg = ctx->infra_base + | ||||
| +			       ctx->data->regs_ofs->vio_shift_sta_offset; | ||||
| +	pd_vio_shift_sel_reg = ctx->infra_base + | ||||
| +			       ctx->data->regs_ofs->vio_shift_sel_offset; | ||||
| +	pd_vio_shift_con_reg = ctx->infra_base + | ||||
| +			       ctx->data->regs_ofs->vio_shift_con_offset; | ||||
| + | ||||
| +	/* Find the minimum shift group which has violation */ | ||||
| +	val = readl(pd_vio_shift_sta_reg); | ||||
| +	if (!val) | ||||
| +		return false; | ||||
| + | ||||
| +	min_shift_group = __ffs(val); | ||||
| + | ||||
| +	/* Assign the group to sync */ | ||||
| +	writel(0x1 << min_shift_group, pd_vio_shift_sel_reg); | ||||
| + | ||||
| +	/* Start syncing */ | ||||
| +	writel(0x1, pd_vio_shift_con_reg); | ||||
| + | ||||
| +	ret = readl_poll_timeout(pd_vio_shift_con_reg, val, val == 0x3, 0, | ||||
| +				 PHY_DEVAPC_TIMEOUT); | ||||
| +	if (ret) { | ||||
| +		dev_err(ctx->dev, "%s: Shift violation info failed\n", __func__); | ||||
| +		return false; | ||||
| +	} | ||||
| + | ||||
| +	/* Stop syncing */ | ||||
| +	writel(0x0, pd_vio_shift_con_reg); | ||||
| + | ||||
| +	/* Write clear */ | ||||
| +	writel(0x1 << min_shift_group, pd_vio_shift_sta_reg); | ||||
| + | ||||
| +	return true; | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * devapc_extract_vio_dbg - extract full violation information after doing | ||||
| + *                          shift mechanism. | ||||
| + */ | ||||
| +static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx) | ||||
| +{ | ||||
| +	struct mtk_devapc_vio_dbgs vio_dbgs; | ||||
| +	void __iomem *vio_dbg0_reg; | ||||
| +	void __iomem *vio_dbg1_reg; | ||||
| + | ||||
| +	vio_dbg0_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg0_offset; | ||||
| +	vio_dbg1_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg1_offset; | ||||
| + | ||||
| +	vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg); | ||||
| +	vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg); | ||||
| + | ||||
| +	/* Print violation information */ | ||||
| +	if (vio_dbgs.dbg0_bits.vio_w) | ||||
| +		dev_info(ctx->dev, "Write Violation\n"); | ||||
| +	else if (vio_dbgs.dbg0_bits.vio_r) | ||||
| +		dev_info(ctx->dev, "Read Violation\n"); | ||||
| + | ||||
| +	dev_info(ctx->dev, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%x\n", | ||||
| +		 vio_dbgs.dbg0_bits.mstid, vio_dbgs.dbg0_bits.dmnid, | ||||
| +		 vio_dbgs.vio_dbg1); | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * devapc_violation_irq - the devapc Interrupt Service Routine (ISR) will dump | ||||
| + *                        violation information including which master violates | ||||
| + *                        access slave. | ||||
| + */ | ||||
| +static irqreturn_t devapc_violation_irq(int irq_number, void *data) | ||||
| +{ | ||||
| +	struct mtk_devapc_context *ctx = data; | ||||
| + | ||||
| +	while (devapc_sync_vio_dbg(ctx)) | ||||
| +		devapc_extract_vio_dbg(ctx); | ||||
| + | ||||
| +	clear_vio_status(ctx); | ||||
| + | ||||
| +	return IRQ_HANDLED; | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * start_devapc - unmask slave's irq to start receiving devapc violation. | ||||
| + */ | ||||
| +static void start_devapc(struct mtk_devapc_context *ctx) | ||||
| +{ | ||||
| +	writel(BIT(31), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset); | ||||
| + | ||||
| +	mask_module_irq(ctx, false); | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * stop_devapc - mask slave's irq to stop service. | ||||
| + */ | ||||
| +static void stop_devapc(struct mtk_devapc_context *ctx) | ||||
| +{ | ||||
| +	mask_module_irq(ctx, true); | ||||
| + | ||||
| +	writel(BIT(2), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset); | ||||
| +} | ||||
| + | ||||
| +static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt6779 = { | ||||
| +	.vio_mask_offset = 0x0, | ||||
| +	.vio_sta_offset = 0x400, | ||||
| +	.vio_dbg0_offset = 0x900, | ||||
| +	.vio_dbg1_offset = 0x904, | ||||
| +	.apc_con_offset = 0xF00, | ||||
| +	.vio_shift_sta_offset = 0xF10, | ||||
| +	.vio_shift_sel_offset = 0xF14, | ||||
| +	.vio_shift_con_offset = 0xF20, | ||||
| +}; | ||||
| + | ||||
| +static const struct mtk_devapc_data devapc_mt6779 = { | ||||
| +	.vio_idx_num = 511, | ||||
| +	.regs_ofs = &devapc_regs_ofs_mt6779, | ||||
| +}; | ||||
| + | ||||
| +static const struct mtk_devapc_data devapc_mt8186 = { | ||||
| +	.vio_idx_num = 519, | ||||
| +	.regs_ofs = &devapc_regs_ofs_mt6779, | ||||
| +}; | ||||
| + | ||||
| +static const struct of_device_id mtk_devapc_dt_match[] = { | ||||
| +	{ | ||||
| +		.compatible = "mediatek,mt6779-devapc", | ||||
| +		.data = &devapc_mt6779, | ||||
| +	}, { | ||||
| +		.compatible = "mediatek,mt8186-devapc", | ||||
| +		.data = &devapc_mt8186, | ||||
| +	}, { | ||||
| +	}, | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, mtk_devapc_dt_match); | ||||
| + | ||||
| +static int mtk_devapc_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct device_node *node = pdev->dev.of_node; | ||||
| +	struct mtk_devapc_context *ctx; | ||||
| +	u32 devapc_irq; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (IS_ERR(node)) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); | ||||
| +	if (!ctx) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	ctx->data = of_device_get_match_data(&pdev->dev); | ||||
| +	ctx->dev = &pdev->dev; | ||||
| + | ||||
| +	ctx->infra_base = of_iomap(node, 0); | ||||
| +	if (!ctx->infra_base) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	devapc_irq = irq_of_parse_and_map(node, 0); | ||||
| +	if (!devapc_irq) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	ctx->infra_clk = devm_clk_get_enabled(&pdev->dev, "devapc-infra-clock"); | ||||
| +	if (IS_ERR(ctx->infra_clk)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	ret = devm_request_irq(&pdev->dev, devapc_irq, devapc_violation_irq, | ||||
| +			       IRQF_TRIGGER_NONE, "devapc", ctx); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	platform_set_drvdata(pdev, ctx); | ||||
| + | ||||
| +	start_devapc(ctx); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int mtk_devapc_remove(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct mtk_devapc_context *ctx = platform_get_drvdata(pdev); | ||||
| + | ||||
| +	stop_devapc(ctx); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static struct platform_driver mtk_devapc_driver = { | ||||
| +	.probe = mtk_devapc_probe, | ||||
| +	.remove = mtk_devapc_remove, | ||||
| +	.driver = { | ||||
| +		.name = "mtk-devapc", | ||||
| +		.of_match_table = mtk_devapc_dt_match, | ||||
| +	}, | ||||
| +}; | ||||
| + | ||||
| +module_platform_driver(mtk_devapc_driver); | ||||
| + | ||||
| +MODULE_DESCRIPTION("Mediatek Device APC Driver"); | ||||
| +MODULE_AUTHOR("Neal Liu <neal.liu@mediatek.com>"); | ||||
| +MODULE_LICENSE("GPL"); | ||||
| @@ -0,0 +1,33 @@ | ||||
| diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig | ||||
| index cf3a53e..be40a84 100644 | ||||
| --- a/drivers/clk/mediatek/Kconfig | ||||
| +++ b/drivers/clk/mediatek/Kconfig | ||||
| @@ -275,6 +275,14 @@ config COMMON_CLK_MT7981 | ||||
|  	  This driver supports MediaTek MT7981 basic clocks and clocks | ||||
|  	  required for various periperals found on MediaTek. | ||||
|   | ||||
| +config COMMON_CLK_MT7987 | ||||
| +	bool "Clock driver for MediaTek MT7987" | ||||
| +	depends on ARCH_MEDIATEK || COMPILE_TEST | ||||
| +	select COMMON_CLK_MEDIATEK | ||||
| +	---help--- | ||||
| +	  This driver supports MediaTek MT7987 basic clocks and clocks | ||||
| +	  required for various periperals found on MediaTek. | ||||
| + | ||||
|  config COMMON_CLK_MT7988 | ||||
|  	bool "Clock driver for MediaTek MT7988" | ||||
|  	depends on ARCH_MEDIATEK || COMPILE_TEST | ||||
| --- a/drivers/clk/mediatek/Makefile | ||||
| +++ b/drivers/clk/mediatek/Makefile | ||||
| @@ -41,6 +41,11 @@ obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o | ||||
|  obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o | ||||
|  obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986.o | ||||
|  obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981.o | ||||
| +obj-$(CONFIG_COMMON_CLK_MT7987) += clk-mt7987-apmixed.o | ||||
| +obj-$(CONFIG_COMMON_CLK_MT7987) += clk-mt7987-topckgen.o | ||||
| +obj-$(CONFIG_COMMON_CLK_MT7987) += clk-mt7987-infracfg.o | ||||
| +obj-$(CONFIG_COMMON_CLK_MT7987) += clk-mt7987-mcusys.o | ||||
| +obj-$(CONFIG_COMMON_CLK_MT7987) += clk-mt7987-eth.o | ||||
|  obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988.o | ||||
|  obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o | ||||
|  obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o | ||||
| @@ -0,0 +1,26 @@ | ||||
| --- a/drivers/pinctrl/mediatek/Kconfig | ||||
| +++ b/drivers/pinctrl/mediatek/Kconfig | ||||
| @@ -112,6 +112,13 @@ config PINCTRL_MT7986 | ||||
|  	default ARM64 && ARCH_MEDIATEK | ||||
|  	select PINCTRL_MTK_MOORE | ||||
|   | ||||
| +config PINCTRL_MT7987 | ||||
| +        bool "Mediatek MT7987 pin control" | ||||
| +        depends on OF | ||||
| +        depends on ARM64 || COMPILE_TEST | ||||
| +        default ARM64 && ARCH_MEDIATEK | ||||
| +        select PINCTRL_MTK_MOORE | ||||
| + | ||||
|  config PINCTRL_MT7988 | ||||
|          bool "Mediatek MT7988 pin control" | ||||
|          depends on OF | ||||
| --- a/drivers/pinctrl/mediatek/Makefile | ||||
| +++ b/drivers/pinctrl/mediatek/Makefile | ||||
| @@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_MT7623)	+= pinctrl- | ||||
|  obj-$(CONFIG_PINCTRL_MT7629)	+= pinctrl-mt7629.o | ||||
|  obj-$(CONFIG_PINCTRL_MT7981)	+= pinctrl-mt7981.o | ||||
|  obj-$(CONFIG_PINCTRL_MT7986)	+= pinctrl-mt7986.o | ||||
| +obj-$(CONFIG_PINCTRL_MT7987)    += pinctrl-mt7987.o | ||||
|  obj-$(CONFIG_PINCTRL_MT7988)    += pinctrl-mt7988.o | ||||
|  obj-$(CONFIG_PINCTRL_MT8173)	+= pinctrl-mt8173.o | ||||
|  obj-$(CONFIG_PINCTRL_MT8183)	+= pinctrl-mt8183.o | ||||
| @@ -164,7 +164,7 @@ index a8bd06da7..75fca4cef 100644 | ||||
| +}; | ||||
| + | ||||
| +static const struct mtk_rng_of_data mt7986_rng_data = { | ||||
| +	.rng_version = 1, | ||||
| +	.rng_version = 2, | ||||
| +}; | ||||
| + | ||||
| +static const struct mtk_rng_of_data mt7623_rng_data = { | ||||
|   | ||||
| @@ -10,11 +10,9 @@ Subject: [PATCH] | ||||
|  drivers/char/tpm/tpm_tis_spi.c  |  7 +++++++ | ||||
|  3 files changed, 28 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c | ||||
| index 70f785994..b9898a56d 100644 | ||||
| --- a/drivers/char/tpm/tpm_tis_core.c | ||||
| +++ b/drivers/char/tpm/tpm_tis_core.c | ||||
| @@ -817,6 +817,21 @@ static const struct tpm_class_ops tpm_tis = { | ||||
| @@ -823,6 +823,21 @@ static const struct tpm_class_ops tpm_ti | ||||
|  	.clk_enable = tpm_tis_clkrun_enable, | ||||
|  }; | ||||
|   | ||||
| @@ -36,19 +34,19 @@ index 70f785994..b9898a56d 100644 | ||||
|  int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, | ||||
|  		      const struct tpm_tis_phy_ops *phy_ops, | ||||
|  		      acpi_handle acpi_dev_handle) | ||||
| @@ -864,6 +879,10 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, | ||||
| @@ -870,6 +885,12 @@ int tpm_tis_core_init(struct device *dev | ||||
|  	if (chip->ops->clk_enable != NULL) | ||||
|  		chip->ops->clk_enable(chip, true); | ||||
|   | ||||
| +	if (phy_ops->do_calibration) { | ||||
| +		rc = priv->phy_ops->do_calibration(priv, dev); | ||||
| +		if (rc) | ||||
| +			goto out_err; | ||||
| +	} | ||||
| + | ||||
|  	if (wait_startup(chip, 0) != 0) { | ||||
|  		rc = -ENODEV; | ||||
|  		goto out_err; | ||||
| diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h | ||||
| index 7337819f5..7bb0bc8b6 100644 | ||||
| --- a/drivers/char/tpm/tpm_tis_core.h | ||||
| +++ b/drivers/char/tpm/tpm_tis_core.h | ||||
| @@ -106,6 +106,7 @@ struct tpm_tis_phy_ops { | ||||
| @@ -67,11 +65,9 @@ index 7337819f5..7bb0bc8b6 100644 | ||||
|  int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, | ||||
|  		      const struct tpm_tis_phy_ops *phy_ops, | ||||
|  		      acpi_handle acpi_dev_handle); | ||||
| diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c | ||||
| index 19513e622..3be2d53a5 100644 | ||||
| --- a/drivers/char/tpm/tpm_tis_spi.c | ||||
| +++ b/drivers/char/tpm/tpm_tis_spi.c | ||||
| @@ -184,12 +184,19 @@ static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) | ||||
| @@ -184,12 +184,19 @@ static int tpm_tis_spi_write32(struct tp | ||||
|  	return rc; | ||||
|  } | ||||
|   | ||||
| @@ -91,6 +87,3 @@ index 19513e622..3be2d53a5 100644 | ||||
|  }; | ||||
|   | ||||
|  static int tpm_tis_spi_probe(struct spi_device *dev) | ||||
| --  | ||||
| 2.34.1 | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,21 @@ | ||||
| --- a/drivers/char/hw_random/mtk-rng.c | ||||
| +++ b/drivers/char/hw_random/mtk-rng.c | ||||
| @@ -231,6 +231,10 @@ static const struct mtk_rng_of_data mt79 | ||||
|  	.rng_version = 2, | ||||
|  }; | ||||
|   | ||||
| +static const struct mtk_rng_of_data mt7987_rng_data = { | ||||
| +	.rng_version = 2, | ||||
| +}; | ||||
| + | ||||
|  static const struct mtk_rng_of_data mt7988_rng_data = { | ||||
|  	.rng_version = 2, | ||||
|  }; | ||||
| @@ -242,6 +246,7 @@ static const struct mtk_rng_of_data mt76 | ||||
|  static const struct of_device_id mtk_rng_match[] = { | ||||
|  	{ .compatible = "mediatek,mt7981-rng", .data = &mt7981_rng_data }, | ||||
|  	{ .compatible = "mediatek,mt7986-rng", .data = &mt7986_rng_data }, | ||||
| +	{ .compatible = "mediatek,mt7987-rng", .data = &mt7987_rng_data }, | ||||
|  	{ .compatible = "mediatek,mt7988-rng", .data = &mt7988_rng_data }, | ||||
|  	{ .compatible = "mediatek,mt7623-rng", .data = &mt7623_rng_data }, | ||||
|  	{}, | ||||
| @@ -0,0 +1,78 @@ | ||||
| From 7f4532a2bffdb0aebcabc2a672c4b97670e002a5 Mon Sep 17 00:00:00 2001 | ||||
| From: Sam Shih <sam.shih@mediatek.com> | ||||
| Date: Mon, 3 Mar 2025 14:33:33 +0800 | ||||
| Subject: [PATCH] add pwm reg-v3 support for mt7987 | ||||
|  | ||||
| --- | ||||
|  drivers/pwm/pwm-mediatek.c | 20 ++++++++++++++++++++ | ||||
|  1 file changed, 20 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c | ||||
| index 79d15a9c..ad7cb2aa 100644 | ||||
| --- a/drivers/pwm/pwm-mediatek.c | ||||
| +++ b/drivers/pwm/pwm-mediatek.c | ||||
| @@ -34,6 +34,7 @@ | ||||
|  #define PWM_CLK_DIV_MAX		7 | ||||
|  #define REG_V1			1 | ||||
|  #define REG_V2			2 | ||||
| +#define REG_V3			3 | ||||
|   | ||||
|  struct pwm_mediatek_of_data { | ||||
|  	unsigned int num_pwms; | ||||
| @@ -67,6 +68,10 @@ static const unsigned int mtk_pwm_reg_offset_v2[] = { | ||||
|  	0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x1c0, 0x200, 0x0240 | ||||
|  }; | ||||
|   | ||||
| +static const unsigned int mtk_pwm_reg_offset_v3[] = { | ||||
| +	0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x600, 0x700, 0x0800 | ||||
| +}; | ||||
| + | ||||
|  static inline struct pwm_mediatek_chip * | ||||
|  to_pwm_mediatek_chip(struct pwm_chip *chip) | ||||
|  { | ||||
| @@ -117,6 +122,10 @@ static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip, | ||||
|  	u32 pwm_offset; | ||||
|   | ||||
|  	switch (chip->soc->reg_ver) { | ||||
| +	case REG_V3: | ||||
| +		pwm_offset = mtk_pwm_reg_offset_v3[num]; | ||||
| +		break; | ||||
| + | ||||
|  	case REG_V2: | ||||
|  		pwm_offset = mtk_pwm_reg_offset_v2[num]; | ||||
|  		break; | ||||
| @@ -136,6 +145,10 @@ static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip, | ||||
|  	u32 pwm_offset; | ||||
|   | ||||
|  	switch (chip->soc->reg_ver) { | ||||
| +	case REG_V3: | ||||
| +		pwm_offset = mtk_pwm_reg_offset_v3[num]; | ||||
| +		break; | ||||
| + | ||||
|  	case REG_V2: | ||||
|  		pwm_offset = mtk_pwm_reg_offset_v2[num]; | ||||
|  		break; | ||||
| @@ -376,6 +389,12 @@ static const struct pwm_mediatek_of_data mt7986_pwm_data = { | ||||
|  	.reg_ver = REG_V1, | ||||
|  }; | ||||
|   | ||||
| +static const struct pwm_mediatek_of_data mt7987_pwm_data = { | ||||
| +	.num_pwms = 3, | ||||
| +	.pwm45_fixup = false, | ||||
| +	.reg_ver = REG_V3, | ||||
| +}; | ||||
| + | ||||
|  static const struct pwm_mediatek_of_data mt7988_pwm_data = { | ||||
|  	.num_pwms = 8, | ||||
|  	.pwm45_fixup = false, | ||||
| @@ -396,6 +415,7 @@ static const struct of_device_id pwm_mediatek_of_match[] = { | ||||
|  	{ .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data }, | ||||
|  	{ .compatible = "mediatek,mt7981-pwm", .data = &mt7981_pwm_data }, | ||||
|  	{ .compatible = "mediatek,mt7986-pwm", .data = &mt7986_pwm_data }, | ||||
| +	{ .compatible = "mediatek,mt7987-pwm", .data = &mt7987_pwm_data }, | ||||
|  	{ .compatible = "mediatek,mt7988-pwm", .data = &mt7988_pwm_data }, | ||||
|  	{ .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data }, | ||||
|  	{ }, | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -0,0 +1,220 @@ | ||||
| From eecc1e7982d4f5da63b129e5437d79ced3aeb26f Mon Sep 17 00:00:00 2001 | ||||
| From: Sam Shih <sam.shih@mediatek.com> | ||||
| Date: Mon, 3 Mar 2025 14:38:41 +0800 | ||||
| Subject: [PATCH] add pwm hw breathing light support for mt7987 | ||||
|  | ||||
| --- | ||||
|  drivers/pwm/pwm-mediatek.c | 118 ++++++++++++++++++++++++++++++++++++- | ||||
|  1 file changed, 116 insertions(+), 2 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c | ||||
| index ad7cb2aa..6e5ba9da 100644 | ||||
| --- a/drivers/pwm/pwm-mediatek.c | ||||
| +++ b/drivers/pwm/pwm-mediatek.c | ||||
| @@ -31,6 +31,11 @@ | ||||
|  #define PWMTHRES		0x30 | ||||
|  #define PWM45THRES_FIXUP	0x34 | ||||
|   | ||||
| +#define PWM_BREATHING_THRES_MAX	32 | ||||
| +#define PWM_BREATHING_CON	0xb8 | ||||
| +#define PWM_BREATHING_THRES_0	0xbc | ||||
| +#define PWM_BREATHING_THRES_1	0xd0 | ||||
| + | ||||
|  #define PWM_CLK_DIV_MAX		7 | ||||
|  #define REG_V1			1 | ||||
|  #define REG_V2			2 | ||||
| @@ -40,6 +45,8 @@ struct pwm_mediatek_of_data { | ||||
|  	unsigned int num_pwms; | ||||
|  	bool pwm45_fixup; | ||||
|  	int reg_ver; | ||||
| +	int hw_breathing_light; | ||||
| +	int hw_breathing_light_thres_num; | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| @@ -58,6 +65,7 @@ struct pwm_mediatek_chip { | ||||
|  	struct clk *clk_main; | ||||
|  	struct clk **clk_pwms; | ||||
|  	const struct pwm_mediatek_of_data *soc; | ||||
| +	bool bw_mode[8]; | ||||
|  }; | ||||
|   | ||||
|  static const unsigned int mtk_pwm_reg_offset_v1[] = { | ||||
| @@ -161,6 +169,85 @@ static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip, | ||||
|  	writel(value, chip->regs + pwm_offset + offset); | ||||
|  } | ||||
|   | ||||
| +static inline int pwm_bl_set_thres(struct pwm_mediatek_chip *chip, int pwm, | ||||
| +				    int idx, int value, int scale) | ||||
| +{ | ||||
| +	u32 offset; | ||||
| +	u32 shift; | ||||
| +	u32 tmp; | ||||
| + | ||||
| +	value = value / scale; | ||||
| +	if ((idx < 0) || (idx >= PWM_BREATHING_THRES_MAX)) | ||||
| +		return -EINVAL; | ||||
| +	if ((value < 0) || (value > 255)) | ||||
| +		return -EINVAL; | ||||
| +	if (idx < 4) { | ||||
| +		offset = PWM_BREATHING_THRES_0; | ||||
| +		shift = (idx % 4) * 8; | ||||
| +	} else { | ||||
| +		offset = PWM_BREATHING_THRES_1 + ((idx / 4) - 1) * 4; | ||||
| +		shift = (idx % 4) * 8; | ||||
| +	} | ||||
| +	tmp = readl(chip->regs + mtk_pwm_reg_offset_v3[pwm] + offset); | ||||
| +	tmp &= ~(0xff << shift); | ||||
| +	tmp |= (value << shift); | ||||
| +	writel(tmp, chip->regs + mtk_pwm_reg_offset_v3[pwm] + offset); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static inline int pwm_config_bl_thres(struct pwm_mediatek_chip *chip, int pwm, | ||||
| +				      int max_counter, int num_thres) | ||||
| +{ | ||||
| +	/* use x_scale to make the wavefrom display smoothly */ | ||||
| +	const int x_scale = 1000; | ||||
| +	int c, x; | ||||
| +	int ret; | ||||
| +	int i; | ||||
| +	/* | ||||
| +	 *  [Breathing Light Pattern] | ||||
| +         *  - x         : max_counter / (num_thres / 2) | ||||
| +         *  - 100% duty : (num_thres / 2) * x | ||||
| +         *  - 0% duty   : 0 * x | ||||
| +         *  - per period: T(0)~T(num_thres) | ||||
| +	 *                (If period = 10^9 (ns), 1T = 10^9 (ns) / num_thres) | ||||
| +         * | ||||
| +	 *  | num_thres | T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 10 11 12 13 14 15 ... | | ||||
| +	 *  |-----------|-----------------------------------------------------| | ||||
| +         *  | 8         | 4x 3x 2x 1x 0x 1x 2x 3x (repeat) .................. | | ||||
| +	 *  | 16        | 8x 7x 6x 5x 4x 3x 2x 1x 0x 1x 2x 3x 4x 5x 6x 7x ... | | ||||
| +	 *  | 24        | 12x ............................................... | | ||||
| +	 *  | 32 (max)  | 16x ............................................... | | ||||
| +	 */ | ||||
| +	int pattern[PWM_BREATHING_THRES_MAX]; | ||||
| + | ||||
| +	if ((num_thres < 2) || (num_thres % 2)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	c = num_thres / 2; | ||||
| +	x = (max_counter * x_scale) / (num_thres / 2); | ||||
| + | ||||
| +	/* create breathing ligh pattern according to previous table */ | ||||
| +	/* use (x_scale * max_counter) instead of (x * num_thres) */ | ||||
| +	pattern[0] = max_counter * x_scale; | ||||
| +	/* caculate each pattern according to (i) */ | ||||
| +	for (i = 1 ; i < c ; i++) | ||||
| +		pattern[i] = x * (c - i); | ||||
| +	for (i = c ; i < num_thres ; i++) | ||||
| +		pattern[i] = x * (i - c); | ||||
| +	for (i = 0 ; i < num_thres ; i++) { | ||||
| +		ret = pwm_bl_set_thres(chip, pwm, i, pattern[i], x_scale); | ||||
| +		if (ret) | ||||
| +			return ret; | ||||
| +	} | ||||
| + | ||||
| +	/* enable breathing light mode */ | ||||
| +	writel(((num_thres - 1) << 8 | 0x1), | ||||
| +	       chip->regs + mtk_pwm_reg_offset_v3[pwm] + PWM_BREATHING_CON); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
|  			       int duty_ns, int period_ns) | ||||
|  { | ||||
| @@ -171,6 +258,8 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
|  	u32 clkdiv = 0, clksel = 0, cnt_period, cnt_duty, | ||||
|  	    reg_width = PWMDWIDTH, reg_thres = PWMTHRES; | ||||
|  	u64 resolution; | ||||
| +	u32 max_cnt_period = 8191; | ||||
| +	int thres_num; | ||||
|  	int ret; | ||||
|   | ||||
|  	ret = pwm_mediatek_clk_enable(chip, pwm); | ||||
| @@ -178,15 +267,24 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
|   | ||||
| +	if (pc->bw_mode[pwm->hwpwm]) { | ||||
| +		thres_num = pc->soc->hw_breathing_light_thres_num; | ||||
| +		period_ns = period_ns / thres_num; | ||||
| +		max_cnt_period = 255; | ||||
| +	} | ||||
| + | ||||
|  	/* Using resolution in picosecond gets accuracy higher */ | ||||
|  	resolution = (u64)NSEC_PER_SEC * 1000; | ||||
| + | ||||
|  	/* Calculate resolution based on current clock frequency */ | ||||
|  	do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); | ||||
| + | ||||
|  	/* Using resolution to calculate cnt_period which represents | ||||
|  	 * the effective range of the PWM period counter | ||||
|  	 */ | ||||
|  	cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); | ||||
| -	while (cnt_period > 8191) { | ||||
| + | ||||
| +	while (cnt_period > max_cnt_period) { | ||||
|  		/* Using clkdiv to reduce clock frequency and calculate | ||||
|  		 * new resolution based on new clock speed | ||||
|  		 */ | ||||
| @@ -230,8 +328,14 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
|  				    clkdiv); | ||||
|  	else | ||||
|  		pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); | ||||
| + | ||||
|  	pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); | ||||
| -	pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); | ||||
| + | ||||
| +	/* use array of bw_thres instead of normal thres in bw_mode */ | ||||
| +	if (pc->bw_mode[pwm->hwpwm]) | ||||
| +		pwm_config_bl_thres(pc, pwm->hwpwm, cnt_period, thres_num); | ||||
| +	else | ||||
| +		pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); | ||||
|   | ||||
|  	pwm_mediatek_clk_disable(chip, pwm); | ||||
|   | ||||
| @@ -276,6 +380,7 @@ static const struct pwm_ops pwm_mediatek_ops = { | ||||
|   | ||||
|  static int pwm_mediatek_probe(struct platform_device *pdev) | ||||
|  { | ||||
| +	struct device_node *np = pdev->dev.of_node; | ||||
|  	struct pwm_mediatek_chip *pc; | ||||
|  	struct resource *res; | ||||
|  	unsigned int i; | ||||
| @@ -313,6 +418,7 @@ static int pwm_mediatek_probe(struct platform_device *pdev) | ||||
|   | ||||
|  	for (i = 0; i < pc->soc->num_pwms; i++) { | ||||
|  		char name[8]; | ||||
| +		char bw_name[32]; | ||||
|   | ||||
|  		snprintf(name, sizeof(name), "pwm%d", i + 1); | ||||
|   | ||||
| @@ -322,6 +428,12 @@ static int pwm_mediatek_probe(struct platform_device *pdev) | ||||
|  				name, PTR_ERR(pc->clk_pwms[i])); | ||||
|  			return PTR_ERR(pc->clk_pwms[i]); | ||||
|  		} | ||||
| + | ||||
| +		if (pc->soc->hw_breathing_light) { | ||||
| +			snprintf(bw_name, sizeof(bw_name), | ||||
| +				 "mediatek,pwm%d-breathing-light", i); | ||||
| +			pc->bw_mode[i] = of_property_read_bool(np, bw_name); | ||||
| +		} | ||||
|  	} | ||||
|   | ||||
|  	platform_set_drvdata(pdev, pc); | ||||
| @@ -393,6 +505,8 @@ static const struct pwm_mediatek_of_data mt7987_pwm_data = { | ||||
|  	.num_pwms = 3, | ||||
|  	.pwm45_fixup = false, | ||||
|  	.reg_ver = REG_V3, | ||||
| +	.hw_breathing_light = 1, | ||||
| +	.hw_breathing_light_thres_num = 32, | ||||
|  }; | ||||
|   | ||||
|  static const struct pwm_mediatek_of_data mt7988_pwm_data = { | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -0,0 +1,34 @@ | ||||
| Index: linux-5.4.281/drivers/mtd/spi-nor/spi-nor.c | ||||
| =================================================================== | ||||
| --- linux-5.4.281.orig/drivers/mtd/spi-nor/spi-nor.c | ||||
| +++ linux-5.4.281/drivers/mtd/spi-nor/spi-nor.c | ||||
| @@ -2242,14 +2242,13 @@ static const struct flash_info spi_nor_i | ||||
|  	{ "en25p64",    INFO(0x1c2017, 0, 64 * 1024,  128, 0) }, | ||||
|  	{ "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) }, | ||||
|  	{ "en25q128",   INFO(0x1c3018, 0, 64 * 1024,  256, SECT_4K) }, | ||||
| -	{ "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256, 0) }, | ||||
|  	{ "en25q80a",   INFO(0x1c3014, 0, 64 * 1024,   16, | ||||
|  			SECT_4K | SPI_NOR_DUAL_READ) }, | ||||
|  	{ "en25qh32",   INFO(0x1c7016, 0, 64 * 1024,   64, 0) }, | ||||
|  	{ "en25qh64",   INFO(0x1c7017, 0, 64 * 1024,  128, | ||||
|  			SECT_4K | SPI_NOR_DUAL_READ) }, | ||||
|  	{ "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256, 0) }, | ||||
| -	{ "en25qx128",	INFO(0x1c7118, 0, 64 * 1024,  256, | ||||
| +	{ "en25qx128a",	INFO(0x1c7118, 0, 64 * 1024,  256, | ||||
|  			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | | ||||
|  			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, | ||||
|  	{ "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, 0) }, | ||||
| @@ -2360,10 +2359,13 @@ static const struct flash_info spi_nor_i | ||||
|  	{ "mx25u4035",   INFO(0xc22533, 0, 64 * 1024,   8, SECT_4K) }, | ||||
|  	{ "mx25u8035",   INFO(0xc22534, 0, 64 * 1024,  16, SECT_4K) }, | ||||
|  	{ "mx25u6435f",  INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, | ||||
| +	{ "mx25l12833f", INFO(0xc22018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
|  	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, | ||||
|  	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, | ||||
|  	{ "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256, | ||||
|  			 SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
| +	{ "mx25l25645g", INFO(0xc22019, 0, 64 * 1024, 512, | ||||
| +			 SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
|  	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, | ||||
|  			 SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) | ||||
|  			 .fixups = &mx25l25635_fixups }, | ||||
| @@ -9,11 +9,9 @@ Subject: [PATCH] | ||||
|  include/linux/spi/spi.h |  42 ++++++++++++ | ||||
|  2 files changed, 183 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c | ||||
| index e562735a3..28bad4a8b 100644 | ||||
| --- a/drivers/spi/spi.c | ||||
| +++ b/drivers/spi/spi.c | ||||
| @@ -1109,6 +1109,74 @@ static int spi_transfer_wait(struct spi_controller *ctlr, | ||||
| @@ -1109,6 +1109,82 @@ static int spi_transfer_wait(struct spi_ | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -32,8 +30,13 @@ index e562735a3..28bad4a8b 100644 | ||||
| +	bool hit; | ||||
| + | ||||
| +	/* Make sure we can start calibration */ | ||||
| +	if(!ctlr->cal_target || !ctlr->cal_rule || !ctlr->append_caldata) | ||||
| +	if(!ctlr->cal_target || !ctlr->cal_rule) { | ||||
| +		return 0; | ||||
| +	} else if(!ctlr->append_caldata) { | ||||
| +		pr_err("%s: calibration is enabled but no controller data.\n", | ||||
| +		       __func__); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| +	datalen = ctlr->cal_rule->datalen; | ||||
| +	addrlen = ctlr->cal_rule->addrlen; | ||||
| + | ||||
| @@ -70,9 +73,12 @@ index e562735a3..28bad4a8b 100644 | ||||
| +			*target->cal_item = DIV_ROUND_CLOSEST(hit_val, total_hit); | ||||
| +			dev_info(&spi->dev, "calibration result: 0x%x", *target->cal_item); | ||||
| +		} else { | ||||
| +			/* We don't return error in this case because you don't know calibration | ||||
| +			 * failure is caused by bus error or wrong calibration data provided by | ||||
| +			 * user or driver. | ||||
| +			 */ | ||||
| +			*target->cal_item = origin; | ||||
| +			dev_warn(&spi->dev, "calibration failed, fallback to default: 0x%x", origin); | ||||
| +			ret = -EIO; | ||||
| +		} | ||||
| + | ||||
| +		list_del(pos); | ||||
| @@ -88,7 +94,7 @@ index e562735a3..28bad4a8b 100644 | ||||
|  static void _spi_transfer_delay_ns(u32 ns) | ||||
|  { | ||||
|  	if (!ns) | ||||
| @@ -1720,6 +1788,75 @@ void spi_flush_queue(struct spi_controller *ctlr) | ||||
| @@ -1720,6 +1796,75 @@ void spi_flush_queue(struct spi_controll | ||||
|  /*-------------------------------------------------------------------------*/ | ||||
|   | ||||
|  #if defined(CONFIG_OF) | ||||
| @@ -164,7 +170,7 @@ index e562735a3..28bad4a8b 100644 | ||||
|  static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, | ||||
|  			   struct device_node *nc) | ||||
|  { | ||||
| @@ -1841,6 +1978,10 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc) | ||||
| @@ -1841,6 +1986,10 @@ of_register_spi_device(struct spi_contro | ||||
|  	if (rc) | ||||
|  		goto err_out; | ||||
|   | ||||
| @@ -175,8 +181,6 @@ index e562735a3..28bad4a8b 100644 | ||||
|  	/* Store a pointer to the node in the device structure */ | ||||
|  	of_node_get(nc); | ||||
|  	spi->dev.of_node = nc; | ||||
| diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h | ||||
| index 7067f85ce..5330cd9b0 100644 | ||||
| --- a/include/linux/spi/spi.h | ||||
| +++ b/include/linux/spi/spi.h | ||||
| @@ -264,6 +264,40 @@ struct spi_driver { | ||||
| @@ -232,7 +236,7 @@ index 7067f85ce..5330cd9b0 100644 | ||||
|  	int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs); | ||||
|  }; | ||||
|   | ||||
| @@ -1369,6 +1408,9 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) | ||||
| @@ -1369,6 +1408,9 @@ spi_register_board_info(struct spi_board | ||||
|  	{ return 0; } | ||||
|  #endif | ||||
|   | ||||
| @@ -242,6 +246,3 @@ index 7067f85ce..5330cd9b0 100644 | ||||
|  /* If you're hotplugging an adapter with devices (parport, usb, etc) | ||||
|   * use spi_new_device() to describe each device.  You can also call | ||||
|   * spi_unregister_device() to start making that device vanish, but | ||||
| --  | ||||
| 2.34.1 | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,162 @@ | ||||
| --- a/drivers/mtd/nand/raw/nand_onfi.c | ||||
| +++ b/drivers/mtd/nand/raw/nand_onfi.c | ||||
| @@ -12,20 +12,14 @@ | ||||
|   * This file contains all ONFI helpers. | ||||
|   */ | ||||
|   | ||||
| +#include <linux/mtd/param.h> | ||||
|  #include <linux/slab.h> | ||||
|   | ||||
|  #include "internals.h" | ||||
|   | ||||
|  u16 onfi_crc16(u16 crc, u8 const *p, size_t len) | ||||
|  { | ||||
| -	int i; | ||||
| -	while (len--) { | ||||
| -		crc ^= *p++ << 8; | ||||
| -		for (i = 0; i < 8; i++) | ||||
| -			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); | ||||
| -	} | ||||
| - | ||||
| -	return crc; | ||||
| +	return nanddev_crc16(crc, p, len); | ||||
|  } | ||||
|   | ||||
|  /* Parse the Extended Parameter Page. */ | ||||
| @@ -104,37 +98,6 @@ ext_out: | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| - * Recover data with bit-wise majority | ||||
| - */ | ||||
| -static void nand_bit_wise_majority(const void **srcbufs, | ||||
| -				   unsigned int nsrcbufs, | ||||
| -				   void *dstbuf, | ||||
| -				   unsigned int bufsize) | ||||
| -{ | ||||
| -	int i, j, k; | ||||
| - | ||||
| -	for (i = 0; i < bufsize; i++) { | ||||
| -		u8 val = 0; | ||||
| - | ||||
| -		for (j = 0; j < 8; j++) { | ||||
| -			unsigned int cnt = 0; | ||||
| - | ||||
| -			for (k = 0; k < nsrcbufs; k++) { | ||||
| -				const u8 *srcbuf = srcbufs[k]; | ||||
| - | ||||
| -				if (srcbuf[i] & BIT(j)) | ||||
| -					cnt++; | ||||
| -			} | ||||
| - | ||||
| -			if (cnt > nsrcbufs / 2) | ||||
| -				val |= BIT(j); | ||||
| -		} | ||||
| - | ||||
| -		((u8 *)dstbuf)[i] = val; | ||||
| -	} | ||||
| -} | ||||
| - | ||||
| -/* | ||||
|   * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. | ||||
|   */ | ||||
|  int nand_onfi_detect(struct nand_chip *chip) | ||||
| @@ -184,7 +147,7 @@ int nand_onfi_detect(struct nand_chip *c | ||||
|  		const void *srcbufs[3] = {p, p + 1, p + 2}; | ||||
|   | ||||
|  		pr_warn("Could not find a valid ONFI parameter page, trying bit-wise majority to recover it\n"); | ||||
| -		nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p, | ||||
| +		nanddev_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p, | ||||
|  				       sizeof(*p)); | ||||
|   | ||||
|  		if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) != | ||||
| --- /dev/null | ||||
| +++ b/drivers/mtd/nand/param.c | ||||
| @@ -0,0 +1,52 @@ | ||||
| +/* SPDX-License-Identifier: GPL-2.0 */ | ||||
| +/* | ||||
| + * Copyright (c) 2023 - Mediatek | ||||
| + * | ||||
| + * Author: SkyLake <SkyLake.Huang@mediatek.com> | ||||
| + */ | ||||
| + | ||||
| +#include <linux/mtd/param.h> | ||||
| + | ||||
| +u16 nanddev_crc16(u16 crc, u8 const *p, size_t len) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	while (len--) { | ||||
| +		crc ^= *p++ << 8; | ||||
| +		for (i = 0; i < 8; i++) | ||||
| +			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); | ||||
| +	} | ||||
| + | ||||
| +	return crc; | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * Recover data with bit-wise majority | ||||
| + */ | ||||
| +void nanddev_bit_wise_majority(const void **srcbufs, | ||||
| +				   unsigned int nsrcbufs, | ||||
| +				   void *dstbuf, | ||||
| +				   unsigned int bufsize) | ||||
| +{ | ||||
| +	int i, j, k; | ||||
| + | ||||
| +	for (i = 0; i < bufsize; i++) { | ||||
| +		u8 val = 0; | ||||
| + | ||||
| +		for (j = 0; j < 8; j++) { | ||||
| +			unsigned int cnt = 0; | ||||
| + | ||||
| +			for (k = 0; k < nsrcbufs; k++) { | ||||
| +				const u8 *srcbuf = srcbufs[k]; | ||||
| + | ||||
| +				if (srcbuf[i] & BIT(j)) | ||||
| +					cnt++; | ||||
| +			} | ||||
| + | ||||
| +			if (cnt > nsrcbufs / 2) | ||||
| +				val |= BIT(j); | ||||
| +		} | ||||
| + | ||||
| +		((u8 *)dstbuf)[i] = val; | ||||
| +	} | ||||
| +} | ||||
| --- /dev/null | ||||
| +++ b/include/linux/mtd/param.h | ||||
| @@ -0,0 +1,22 @@ | ||||
| +/* SPDX-License-Identifier: GPL-2.0 */ | ||||
| +/* | ||||
| + * Copyright (c) 2023 - Mediatek | ||||
| + * | ||||
| + * Author: SkyLake <SkyLake.Huang@mediatek.com> | ||||
| + */ | ||||
| + | ||||
| +#ifndef __LINUX_NAND_PARAM | ||||
| +#define __LINUX_NAND_PARAM | ||||
| + | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/types.h> | ||||
| +#include <stddef.h> | ||||
| + | ||||
| +u16 nanddev_crc16(u16 crc, u8 const *p, size_t len); | ||||
| +void nanddev_bit_wise_majority(const void **srcbufs, | ||||
| +				   unsigned int nsrcbufs, | ||||
| +				   void *dstbuf, | ||||
| +				   unsigned int bufsize); | ||||
| + | ||||
| +#endif /* __LINUX_NAND_PARAM */ | ||||
| + | ||||
| --- a/drivers/mtd/nand/Makefile | ||||
| +++ b/drivers/mtd/nand/Makefile | ||||
| @@ -1,6 +1,6 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0 | ||||
|   | ||||
| -nandcore-objs := core.o bbt.o | ||||
| +nandcore-objs := core.o bbt.o param.o | ||||
|  obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o | ||||
|   | ||||
|  obj-y	+= onenand/ | ||||
| @@ -0,0 +1,89 @@ | ||||
| --- a/drivers/mtd/spi-nor/spi-nor.c | ||||
| +++ b/drivers/mtd/spi-nor/spi-nor.c | ||||
| @@ -2252,7 +2252,7 @@ static const struct flash_info spi_nor_i | ||||
|  	{ "en25qx128",	INFO(0x1c7118, 0, 64 * 1024,  256, | ||||
|  			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | | ||||
|  			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, | ||||
| -	{ "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, 0) }, | ||||
| +	{ "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, SPI_NOR_4B_OPCODES) }, | ||||
|  	{ "en25qx256a", INFO(0x1c7119, 0, 64 * 1024,  512, | ||||
|  			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | | ||||
|  			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, | ||||
| @@ -4904,7 +4904,6 @@ static void spi_nor_debugfs_init(struct | ||||
|   | ||||
|  static int spi_nor_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) | ||||
|  { | ||||
| -	int ret; | ||||
|  	struct spi_nor *nor = (struct spi_nor *)priv; | ||||
|   | ||||
|  	nor->reg_proto = SNOR_PROTO_1_1_1; | ||||
| @@ -4919,7 +4918,6 @@ static int spi_nor_cal_read(void *priv, | ||||
|  static int spi_nor_cal_read_4B(void *priv, u32 *addr, int addrlen, u8 *buf, | ||||
|  			    int readlen) | ||||
|  { | ||||
| -	int ret; | ||||
|  	struct spi_nor *nor = (struct spi_nor *)priv; | ||||
|   | ||||
|  	nor->reg_proto = SNOR_PROTO_1_1_1; | ||||
| @@ -4971,6 +4969,15 @@ static const struct flash_info *spi_nor_ | ||||
|  	return info; | ||||
|  } | ||||
|   | ||||
| +void spi_nor_reset_read(struct spi_nor *nor) | ||||
| +{ | ||||
| +	nor->reg_proto = SNOR_PROTO_1_1_1; | ||||
| +	nor->read_proto = SNOR_PROTO_1_1_1; | ||||
| +	nor->write_proto = SNOR_PROTO_1_1_1; | ||||
| +	nor->addr_width = 0; | ||||
| +	nor->read_dummy = 0; | ||||
| +} | ||||
| + | ||||
|  int spi_nor_scan(struct spi_nor *nor, const char *name, | ||||
|  		 const struct spi_nor_hwcaps *hwcaps) | ||||
|  { | ||||
| @@ -5009,11 +5016,11 @@ int spi_nor_scan(struct spi_nor *nor, co | ||||
|  		ret = spi_mem_do_calibration(nor->spimem, | ||||
|  					     spi_nor_cal_read, nor); | ||||
|  		if (ret) { | ||||
| +			dev_info(dev, "Switch to 4B mode to do SPI calibration\n"); | ||||
|  			ret = spi_mem_do_calibration(nor->spimem, | ||||
|  						     spi_nor_cal_read_4B, nor); | ||||
| -			if (ret) | ||||
| -				return ret; | ||||
|  		} | ||||
| +		spi_nor_reset_read(nor); | ||||
|  	} | ||||
|   | ||||
|  	info = spi_nor_get_flash_info(nor, name); | ||||
| --- a/drivers/spi/spi.c | ||||
| +++ b/drivers/spi/spi.c | ||||
| @@ -1177,6 +1177,7 @@ int spi_do_calibration(struct spi_contro | ||||
|  			 */ | ||||
|  			*target->cal_item = origin; | ||||
|  			dev_warn(&spi->dev, "calibration failed, fallback to default: 0x%x", origin); | ||||
| +			ret = -EIO; | ||||
|  		} | ||||
|   | ||||
|  		list_del(pos); | ||||
| --- a/drivers/char/tpm/tpm_tis_core.c | ||||
| +++ b/drivers/char/tpm/tpm_tis_core.c | ||||
| @@ -888,7 +888,7 @@ int tpm_tis_core_init(struct device *dev | ||||
|  	if (phy_ops->do_calibration) { | ||||
|  		rc = priv->phy_ops->do_calibration(priv, dev); | ||||
|  		if (rc) | ||||
| -			goto out_err; | ||||
| +			dev_info(dev, "Use default SPI calibration value.\n"); | ||||
|  	} | ||||
|   | ||||
|  	if (wait_startup(chip, 0) != 0) { | ||||
| --- a/drivers/mtd/nand/spi/core.c | ||||
| +++ b/drivers/mtd/nand/spi/core.c | ||||
| @@ -1779,7 +1779,7 @@ static int spinand_init(struct spinand_d | ||||
|   | ||||
|  	ret = spi_mem_do_calibration(spinand->spimem, spinand_cal_read, spinand); | ||||
|  	if (ret) | ||||
| -		dev_err(dev, "Failed to calibrate SPI-NAND (err = %d)\n", ret); | ||||
| +		dev_info(dev, "Use default SPI calibration value.\n"); | ||||
|   | ||||
|  	ret = spinand_detect(spinand); | ||||
|  	if (ret) | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,40 @@ | ||||
| diff --git a/drivers/mtd/mtdsplit/mtdsplit_squashfs.c b/drivers/mtd/mtdsplit/mtdsplit_squashfs.c | ||||
| index f6353da..6e5c569 100644 | ||||
| --- a/drivers/mtd/mtdsplit/mtdsplit_squashfs.c | ||||
| +++ b/drivers/mtd/mtdsplit/mtdsplit_squashfs.c | ||||
| @@ -30,6 +30,9 @@ mtdsplit_parse_squashfs(struct mtd_info *master, | ||||
|  	struct mtd_info *parent_mtd; | ||||
|  	size_t part_offset; | ||||
|  	size_t squashfs_len; | ||||
| +	size_t dm_off, dm_len; | ||||
| +	size_t retlen; | ||||
| +	char verity[7]; | ||||
|  	int err; | ||||
|   | ||||
|  	err = mtd_get_squashfs_len(master, 0, &squashfs_len); | ||||
| @@ -39,6 +42,25 @@ mtdsplit_parse_squashfs(struct mtd_info *master, | ||||
|  	parent_mtd = mtd_get_master(master); | ||||
|  	part_offset = mtdpart_get_offset(master); | ||||
|   | ||||
| +#define DM_VERITY_STR		"verity" | ||||
| +#define DM_VERITY_BLK_SZ	4096 | ||||
| +	/* Try to find DM-verity */ | ||||
| +	dm_off = roundup(squashfs_len, DM_VERITY_BLK_SZ); | ||||
| +	dm_len = 7; | ||||
| +	err = mtd_read(master, dm_off, dm_len, &retlen, (void *)&verity); | ||||
| +	if (err || (retlen != dm_len)) { | ||||
| +		pr_alert("error occured while reading from \"%s\"\n", | ||||
| +			 master->name); | ||||
| +		return -EIO; | ||||
| +	} | ||||
| +	if (!strcmp(verity, DM_VERITY_STR)) { | ||||
| +		int dm_sz; | ||||
| + | ||||
| +		dm_sz = roundup(squashfs_len / 128, DM_VERITY_BLK_SZ); | ||||
| +		dm_sz += DM_VERITY_BLK_SZ * 2; | ||||
| +		squashfs_len += dm_sz; | ||||
| +	} | ||||
| + | ||||
|  	part = kzalloc(sizeof(*part), GFP_KERNEL); | ||||
|  	if (!part) { | ||||
|  		pr_alert("unable to allocate memory for \"%s\" partition\n", | ||||
| @@ -0,0 +1,403 @@ | ||||
| diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c | ||||
| index c229451..9ae8840 100644 | ||||
| --- a/drivers/cpufreq/mediatek-cpufreq.c | ||||
| +++ b/drivers/cpufreq/mediatek-cpufreq.c | ||||
| @@ -10,6 +10,7 @@ | ||||
|  #include <linux/cpumask.h> | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/of.h> | ||||
| +#include <linux/of_platform.h> | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/pm_opp.h> | ||||
|  #include <linux/regulator/consumer.h> | ||||
| @@ -22,6 +23,21 @@ | ||||
|  #define MAX_VOLT_LIMIT		(1150000) | ||||
|  #define VOLT_TOL		(10000) | ||||
|   | ||||
| +struct mtk_cpufreq_vspec { | ||||
| +	unsigned int freq; | ||||
| +	unsigned int vbase; | ||||
| +	unsigned int vscale; | ||||
| +	unsigned int vmax; | ||||
| +}; | ||||
| + | ||||
| +struct mtk_cpufreq_platform_data { | ||||
| +	const struct mtk_cpufreq_vspec *vspec; | ||||
| +	unsigned char adjust_voltage; | ||||
| +	unsigned char has_cci_clk; | ||||
| +	unsigned int cpu_clk_mult; | ||||
| +	unsigned int cci_clk_mult; | ||||
| +}; | ||||
| + | ||||
|  /* | ||||
|   * The struct mtk_cpu_dvfs_info holds necessary information for doing CPU DVFS | ||||
|   * on each CPU power/clock domain of Mediatek SoCs. Each CPU cluster in | ||||
| @@ -34,6 +50,7 @@ | ||||
|   * the original PLL becomes stable at target frequency. | ||||
|   */ | ||||
|  struct mtk_cpu_dvfs_info { | ||||
| +	const struct mtk_cpufreq_platform_data *soc_data; | ||||
|  	struct cpumask cpus; | ||||
|  	struct device *cpu_dev; | ||||
|  	struct regulator *proc_reg; | ||||
| @@ -194,6 +211,9 @@ static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info, | ||||
|   | ||||
|  static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc) | ||||
|  { | ||||
| +	if (!info->soc_data->adjust_voltage) | ||||
| +		return 0; | ||||
| + | ||||
|  	if (info->need_voltage_tracking) | ||||
|  		return mtk_cpufreq_voltage_tracking(info, vproc); | ||||
|  	else | ||||
| @@ -219,7 +239,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, | ||||
|   | ||||
|  	old_freq_hz = clk_get_rate(cpu_clk); | ||||
|   | ||||
| -	if (!IS_ERR(info->cci_clk)) { | ||||
| +	if (info->soc_data->has_cci_clk) { | ||||
|  		cci_clk = info->cci_clk; | ||||
|  		ccipll = clk_get_parent(cci_clk); | ||||
|  		cci_old_freq_hz = clk_get_rate(cci_clk); | ||||
| @@ -231,8 +251,14 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, | ||||
|  		return old_vproc; | ||||
|  	} | ||||
|   | ||||
| -	freq_hz = freq_table[index].frequency * 1000; | ||||
| -	cci_freq_hz = freq_table[index].frequency * 600; | ||||
| +	/* Caculate the target frequency for armpll (cpupll) and ccipll | ||||
| +         * The values of freq_table[index] are recorded in Khz | ||||
| +         * The target frequency for armpll = frequency * cpu_clk_mult (Hz) | ||||
| +         * The target frequency for ccipll = frequency * cci_clk_mult (Hz) | ||||
| +         */ | ||||
| +	freq_hz = freq_table[index].frequency * info->soc_data->cpu_clk_mult; | ||||
| +	cci_freq_hz = | ||||
| +		freq_table[index].frequency * info->soc_data->cci_clk_mult; | ||||
|   | ||||
|  	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); | ||||
|  	if (IS_ERR(opp)) { | ||||
| @@ -259,7 +285,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, | ||||
|  	} | ||||
|   | ||||
|  	/* Reparent the CCI clock to intermediate clock. */ | ||||
| -	if (!IS_ERR(cci_clk)) { | ||||
| +	if (info->soc_data->has_cci_clk) { | ||||
|  		ret = clk_set_parent(cci_clk, info->inter_clk); | ||||
|  		if (ret) { | ||||
|  			pr_err("cpu%d: failed to re-parent cci clock!\n", | ||||
| @@ -291,7 +317,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, | ||||
|  	} | ||||
|   | ||||
|  	/* Set the original PLL to target rate. */ | ||||
| -	if (!IS_ERR(cci_clk)) { | ||||
| +	if (info->soc_data->has_cci_clk) { | ||||
|  		ret = clk_set_rate(ccipll, cci_freq_hz); | ||||
|  		if (ret) { | ||||
|  			pr_err("cpu%d: failed to scale cci clock rate!\n", | ||||
| @@ -313,7 +339,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, | ||||
|  	} | ||||
|   | ||||
|  	/* Set parent of CCI clock back to the original PLL. */ | ||||
| -	if (!IS_ERR(cci_clk)) { | ||||
| +	if (info->soc_data->has_cci_clk) { | ||||
|  		ret = clk_set_parent(cci_clk, ccipll); | ||||
|  		if (ret) { | ||||
|  			pr_err("cpu%d: failed to re-parent cci clock!\n", | ||||
| @@ -332,18 +358,18 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, | ||||
|  		if (ret) { | ||||
|  			pr_err("cpu%d: failed to scale down voltage!\n", | ||||
|  			       policy->cpu); | ||||
| -			if (!IS_ERR(cci_clk)) | ||||
| +			if (info->soc_data->has_cci_clk) | ||||
|  				clk_set_parent(cci_clk, info->inter_clk); | ||||
|   | ||||
|  			clk_set_parent(cpu_clk, info->inter_clk); | ||||
|  			clk_set_rate(armpll, old_freq_hz); | ||||
|   | ||||
| -			if (!IS_ERR(cci_clk)) | ||||
| +			if (info->soc_data->has_cci_clk) | ||||
|  				clk_set_rate(ccipll, cci_old_freq_hz); | ||||
|   | ||||
|  			clk_set_parent(cpu_clk, armpll); | ||||
|   | ||||
| -			if (!IS_ERR(cci_clk)) | ||||
| +			if (info->soc_data->has_cci_clk) | ||||
|  				clk_set_parent(cci_clk, ccipll); | ||||
|   | ||||
|  			return ret; | ||||
| @@ -397,7 +423,21 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) | ||||
|  		goto out_free_resources; | ||||
|  	} | ||||
|   | ||||
| -	cci_clk = clk_get(cpu_dev, "cci"); | ||||
| +	if (info->soc_data->has_cci_clk) { | ||||
| +		cci_clk = clk_get(cpu_dev, "cci"); | ||||
| + | ||||
| +		if (IS_ERR(cci_clk)) { | ||||
| +			if (PTR_ERR(cci_clk) == -EPROBE_DEFER) | ||||
| +				pr_warn("cci clk for cpu%d not ready, retry.\n", | ||||
| +					cpu); | ||||
| +			else | ||||
| +				pr_err("failed to get cci clk for cpu%d\n", | ||||
| +				       cpu); | ||||
| + | ||||
| +			ret = PTR_ERR(cci_clk); | ||||
| +			return ret; | ||||
| +		} | ||||
| +	} | ||||
|   | ||||
|  	proc_reg = regulator_get_optional(cpu_dev, "proc"); | ||||
|  	if (IS_ERR(proc_reg)) { | ||||
| @@ -444,7 +484,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) | ||||
|  	if (ret) | ||||
|  		goto out_disable_mux_clock; | ||||
|   | ||||
| -	if(!(IS_ERR(cci_clk))) { | ||||
| +	if (info->soc_data->has_cci_clk) { | ||||
|  		ret = clk_prepare_enable(cci_clk); | ||||
|  		if(ret) | ||||
|  			goto out_disable_inter_clock; | ||||
| @@ -465,7 +505,10 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) | ||||
|  	info->proc_reg = proc_reg; | ||||
|  	info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg; | ||||
|  	info->cpu_clk = cpu_clk; | ||||
| -	info->cci_clk = cci_clk; | ||||
| + | ||||
| +	if (info->soc_data->has_cci_clk) | ||||
| +		info->cci_clk = cci_clk; | ||||
| + | ||||
|  	info->inter_clk = inter_clk; | ||||
|   | ||||
|  	/* | ||||
| @@ -477,7 +520,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) | ||||
|  	return 0; | ||||
|   | ||||
|  out_disable_cci_clock: | ||||
| -	if(!IS_ERR(cci_clk)) | ||||
| +	if (info->soc_data->has_cci_clk) | ||||
|  		clk_disable_unprepare(cci_clk); | ||||
|   | ||||
|  out_disable_inter_clock: | ||||
| @@ -505,8 +548,9 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) | ||||
|  		clk_put(cpu_clk); | ||||
|  	if (!IS_ERR(inter_clk)) | ||||
|  		clk_put(inter_clk); | ||||
| -	if (!IS_ERR(cci_clk)) | ||||
| -		clk_put(cci_clk); | ||||
| +	if (info->soc_data->has_cci_clk) | ||||
| +		if (!IS_ERR(cci_clk)) | ||||
| +			clk_put(cci_clk); | ||||
|   | ||||
|  	return ret; | ||||
|  } | ||||
| @@ -527,24 +571,67 @@ static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) | ||||
|  		clk_disable_unprepare(info->inter_clk); | ||||
|  		clk_put(info->inter_clk); | ||||
|  	} | ||||
| -	if (!IS_ERR(info->cci_clk)){ | ||||
| -		clk_disable_unprepare(info->cci_clk); | ||||
| -		clk_put(info->cci_clk); | ||||
| +	if (info->soc_data->has_cci_clk) { | ||||
| +		if (!IS_ERR(info->cci_clk)) { | ||||
| +			clk_disable_unprepare(info->cci_clk); | ||||
| +			clk_put(info->cci_clk); | ||||
| +		} | ||||
|  	} | ||||
|   | ||||
|  	dev_pm_opp_of_cpumask_remove_table(&info->cpus); | ||||
|  } | ||||
|   | ||||
| +static int mtk_cpufreq_adjust_voltage(struct mtk_cpu_dvfs_info *info, | ||||
| +				      struct cpufreq_policy *policy) | ||||
| +{ | ||||
| +	const struct mtk_cpufreq_vspec *vspec; | ||||
| +	unsigned int target_voltage; | ||||
| +	struct nvmem_cell *cell; | ||||
| +	struct dev_pm_opp *opp; | ||||
| +	unsigned int cal_data; | ||||
| +	const u8 *buf; | ||||
| +	size_t len; | ||||
| +	int ret; | ||||
| +	int i; | ||||
| + | ||||
| +	cell = nvmem_cell_get(info->cpu_dev, "calibration-data"); | ||||
| +	if (IS_ERR(cell)) | ||||
| +		return PTR_ERR(cell); | ||||
| + | ||||
| +	buf = nvmem_cell_read(cell, &len); | ||||
| +	nvmem_cell_put(cell); | ||||
| +	if (IS_ERR(buf)) | ||||
| +		return PTR_ERR(buf); | ||||
| + | ||||
| +	cal_data = buf[0] & 0x1f; | ||||
| +	pr_debug("%s: read vbinning value: %d\n", __func__, cal_data); | ||||
| +	kfree(buf); | ||||
| +	if (!info->soc_data->vspec) { | ||||
| +		pr_err("voltage spec not found\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	vspec = &info->soc_data->vspec[0]; | ||||
| +	for (i = 0 ; i < vspec->freq ; i++) { | ||||
| +		target_voltage =  vspec->vbase + cal_data * vspec->vscale; | ||||
| +		if (target_voltage > vspec->vmax) { | ||||
| +			pr_warn("freq %u exceeds max voltage\n", vspec->freq); | ||||
| +			pr_warn("force update voltage to %u\n", vspec->vmax); | ||||
| +			target_voltage = vspec->vmax; | ||||
| +		} | ||||
| +		dev_pm_opp_remove(info->cpu_dev, vspec->freq); | ||||
| +		dev_pm_opp_add(info->cpu_dev, vspec->freq, target_voltage); | ||||
| +		vspec = &info->soc_data->vspec[i + 1]; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int mtk_cpufreq_init(struct cpufreq_policy *policy) | ||||
|  { | ||||
|  	struct mtk_cpu_dvfs_info *info; | ||||
|  	struct cpufreq_frequency_table *freq_table; | ||||
|  	int ret; | ||||
| -	int target_vproc; | ||||
| -	u8 reg_val; | ||||
| -	struct nvmem_cell *cell; | ||||
| -	size_t len; | ||||
| -	u8 *buf; | ||||
|   | ||||
|  	info = mtk_cpu_dvfs_info_lookup(policy->cpu); | ||||
|  	if (!info) { | ||||
| @@ -553,19 +640,12 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy) | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| -	cell = nvmem_cell_get(info->cpu_dev, "calibration-data"); | ||||
| -	if (!IS_ERR(cell)) { | ||||
| -		buf = (u8 *)nvmem_cell_read(cell, &len); | ||||
| -		nvmem_cell_put(cell); | ||||
| -		if (!IS_ERR(buf)) { | ||||
| -			reg_val = buf[0] & 0x1f; | ||||
| -			pr_debug("%s: read vbinning value: %d\n", __func__, reg_val); | ||||
| -			if (reg_val > 0) { | ||||
| -				target_vproc = 850000 + reg_val * 10000; | ||||
| -				dev_pm_opp_remove(info->cpu_dev, 1800000000); | ||||
| -				dev_pm_opp_add(info->cpu_dev, 1800000000, target_vproc); | ||||
| -			} | ||||
| -			kfree(buf); | ||||
| +	if (info->soc_data->adjust_voltage) { | ||||
| +		ret = mtk_cpufreq_adjust_voltage(info, policy); | ||||
| +		if (ret) { | ||||
| +			pr_err("failed to adjust voltage for cpu%d: %d\n", | ||||
| +			       policy->cpu, ret); | ||||
| +			return ret; | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| @@ -610,9 +690,17 @@ static struct cpufreq_driver mtk_cpufreq_driver = { | ||||
|   | ||||
|  static int mtk_cpufreq_probe(struct platform_device *pdev) | ||||
|  { | ||||
| +	const struct mtk_cpufreq_platform_data *data; | ||||
|  	struct mtk_cpu_dvfs_info *info, *tmp; | ||||
|  	int cpu, ret; | ||||
|   | ||||
| +	data = dev_get_platdata(&pdev->dev); | ||||
| +	if (!data) { | ||||
| +		dev_err(&pdev->dev, | ||||
| +			"failed to get mtk cpufreq platform data\n"); | ||||
| +		return -ENODEV; | ||||
| +	} | ||||
| + | ||||
|  	for_each_possible_cpu(cpu) { | ||||
|  		info = mtk_cpu_dvfs_info_lookup(cpu); | ||||
|  		if (info) | ||||
| @@ -624,6 +712,7 @@ static int mtk_cpufreq_probe(struct platform_device *pdev) | ||||
|  			goto release_dvfs_info_list; | ||||
|  		} | ||||
|   | ||||
| +		info->soc_data = data; | ||||
|  		ret = mtk_cpu_dvfs_info_init(info, cpu); | ||||
|  		if (ret) { | ||||
|  			dev_err(&pdev->dev, | ||||
| @@ -659,20 +748,43 @@ static struct platform_driver mtk_cpufreq_platdrv = { | ||||
|  	.probe		= mtk_cpufreq_probe, | ||||
|  }; | ||||
|   | ||||
| +struct mtk_cpufreq_vspec mt7988_voltage_spec[] = { | ||||
| +	{ | ||||
| +		.freq = 1800000000, | ||||
| +		.vbase = 850000, | ||||
| +		.vscale = 10000, | ||||
| +		.vmax = 1120000, | ||||
| +	}, | ||||
| +	{ } /* sentinel */ | ||||
| +}; | ||||
| + | ||||
| +static const struct mtk_cpufreq_platform_data mt2701_platform_data = { | ||||
| +	.adjust_voltage = 0, | ||||
| +	.has_cci_clk = 0, | ||||
| +	.cpu_clk_mult = 1000, | ||||
| +}; | ||||
| + | ||||
| +static const struct mtk_cpufreq_platform_data mt7988_platform_data = { | ||||
| +	.vspec = mt7988_voltage_spec, | ||||
| +	.adjust_voltage = 1, | ||||
| +	.has_cci_clk = 1, | ||||
| +	.cpu_clk_mult = 1000, | ||||
| +	.cci_clk_mult = 600, | ||||
| +}; | ||||
| + | ||||
|  /* List of machines supported by this driver */ | ||||
|  static const struct of_device_id mtk_cpufreq_machines[] __initconst = { | ||||
| -	{ .compatible = "mediatek,mt2701", }, | ||||
| -	{ .compatible = "mediatek,mt2712", }, | ||||
| -	{ .compatible = "mediatek,mt7622", }, | ||||
| -	{ .compatible = "mediatek,mt7623", }, | ||||
| -	{ .compatible = "mediatek,mt817x", }, | ||||
| -	{ .compatible = "mediatek,mt8173", }, | ||||
| -	{ .compatible = "mediatek,mt8176", }, | ||||
| -	{ .compatible = "mediatek,mt8183", }, | ||||
| -	{ .compatible = "mediatek,mt8516", }, | ||||
| -	{ .compatible = "mediatek,mt7988", }, | ||||
| - | ||||
| -	{ } | ||||
| +	{ .compatible = "mediatek,mt2701", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt2712", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt7622", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt7623", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt817x", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt8173", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt8176", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt8183", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt8516", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt7988", .data = &mt7988_platform_data }, | ||||
| +	{} | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines); | ||||
|   | ||||
| @@ -680,6 +792,7 @@ static int __init mtk_cpufreq_driver_init(void) | ||||
|  { | ||||
|  	struct device_node *np; | ||||
|  	const struct of_device_id *match; | ||||
| +	const struct mtk_cpufreq_platform_data *data; | ||||
|  	struct platform_device *pdev; | ||||
|  	int err; | ||||
|   | ||||
| @@ -694,6 +807,8 @@ static int __init mtk_cpufreq_driver_init(void) | ||||
|  		return -ENODEV; | ||||
|  	} | ||||
|   | ||||
| +	data = match->data; | ||||
| + | ||||
|  	err = platform_driver_register(&mtk_cpufreq_platdrv); | ||||
|  	if (err) | ||||
|  		return err; | ||||
| @@ -704,7 +819,8 @@ static int __init mtk_cpufreq_driver_init(void) | ||||
|  	 * and the device registration codes are put here to handle defer | ||||
|  	 * probing. | ||||
|  	 */ | ||||
| -	pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0); | ||||
| +	pdev = platform_device_register_data(NULL, "mtk-cpufreq", -1, data, | ||||
| +					     sizeof(*data)); | ||||
|  	if (IS_ERR(pdev)) { | ||||
|  		pr_err("failed to register mtk-cpufreq platform device\n"); | ||||
|  		platform_driver_unregister(&mtk_cpufreq_platdrv); | ||||
| @@ -0,0 +1,12 @@ | ||||
| diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c | ||||
| index 9ae8840..1d0d8c3 100644 | ||||
| --- a/drivers/cpufreq/mediatek-cpufreq.c | ||||
| +++ b/drivers/cpufreq/mediatek-cpufreq.c | ||||
| @@ -783,6 +783,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = { | ||||
|  	{ .compatible = "mediatek,mt8176", .data = &mt2701_platform_data }, | ||||
|  	{ .compatible = "mediatek,mt8183", .data = &mt2701_platform_data }, | ||||
|  	{ .compatible = "mediatek,mt8516", .data = &mt2701_platform_data }, | ||||
| +	{ .compatible = "mediatek,mt7987", .data = &mt2701_platform_data }, | ||||
|  	{ .compatible = "mediatek,mt7988", .data = &mt7988_platform_data }, | ||||
|  	{} | ||||
|  }; | ||||
| @@ -0,0 +1,30 @@ | ||||
| --- a/drivers/iio/pressure/Kconfig | ||||
| +++ b/drivers/iio/pressure/Kconfig | ||||
| @@ -71,6 +71,17 @@ config ZTS8032 | ||||
|  	help | ||||
|  	  Support for the Zilltek ZTS8032 barometric pressure sensor. | ||||
|   | ||||
| +config ZTS8232 | ||||
| +        tristate "Zilltek ZTS8232 pressure and temperature sensor" | ||||
| +        depends on I2C | ||||
| +        select REGMAP_I2C | ||||
| +        help | ||||
| +          Support for the Zilltek ZTS8232 digital barometric pressure sensor. | ||||
| +          It can be accessed over I2C bus. | ||||
| + | ||||
| +          This driver can also be built as a module.  If so, the module will be | ||||
| +          called zts8232. | ||||
| + | ||||
|  config HID_SENSOR_PRESS | ||||
|  	depends on HID_SENSOR_HUB | ||||
|  	select IIO_BUFFER | ||||
| --- a/drivers/iio/pressure/Makefile | ||||
| +++ b/drivers/iio/pressure/Makefile | ||||
| @@ -11,6 +11,7 @@ obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o | ||||
|  obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o | ||||
|  obj-$(CONFIG_DPS310) += dps310.o | ||||
|  obj-$(CONFIG_ZTS8032) += zts8032.o | ||||
| +obj-$(CONFIG_ZTS8232) += zts8232.o | ||||
|  obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o | ||||
|  obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o | ||||
|  obj-$(CONFIG_HP03) += hp03.o | ||||
| @@ -0,0 +1,183 @@ | ||||
| --- a/drivers/soc/mediatek/mtk-devapc.c | ||||
| +++ b/drivers/soc/mediatek/mtk-devapc.c | ||||
| @@ -9,13 +9,14 @@ | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/of.h> | ||||
| +#include <linux/of_device.h> | ||||
|  #include <linux/of_irq.h> | ||||
|  #include <linux/of_address.h> | ||||
|   | ||||
|  #define VIO_MOD_TO_REG_IND(m)	((m) / 32) | ||||
|  #define VIO_MOD_TO_REG_OFF(m)	((m) % 32) | ||||
|   | ||||
| -struct mtk_devapc_vio_dbgs { | ||||
| +struct mtk_devapc_vio_dbgs_v1 { | ||||
|  	union { | ||||
|  		u32 vio_dbg0; | ||||
|  		struct { | ||||
| @@ -31,12 +32,29 @@ struct mtk_devapc_vio_dbgs { | ||||
|  	u32 vio_dbg1; | ||||
|  }; | ||||
|   | ||||
| +struct mtk_devapc_vio_dbgs_v2 { | ||||
| +	union { | ||||
| +		u32 vio_dbg0; | ||||
| +		struct { | ||||
| +			u32 dmnid:6; | ||||
| +			u32 vio_w:1; | ||||
| +			u32 vio_r:1; | ||||
| +		} dbg0_bits; | ||||
| +	}; | ||||
| + | ||||
| +	u32 vio_dbg1; | ||||
| +	u32 vio_dbg2; | ||||
| +	u32 vio_dbg3; | ||||
| +}; | ||||
| + | ||||
|  struct mtk_devapc_regs_ofs { | ||||
|  	/* reg offset */ | ||||
|  	u32 vio_mask_offset; | ||||
|  	u32 vio_sta_offset; | ||||
|  	u32 vio_dbg0_offset; | ||||
|  	u32 vio_dbg1_offset; | ||||
| +	u32 vio_dbg2_offset; | ||||
| +	u32 vio_dbg3_offset; | ||||
|  	u32 apc_con_offset; | ||||
|  	u32 vio_shift_sta_offset; | ||||
|  	u32 vio_shift_sel_offset; | ||||
| @@ -46,6 +64,7 @@ struct mtk_devapc_regs_ofs { | ||||
|  struct mtk_devapc_data { | ||||
|  	/* numbers of violation index */ | ||||
|  	u32 vio_idx_num; | ||||
| +	u32 version; | ||||
|  	const struct mtk_devapc_regs_ofs *regs_ofs; | ||||
|  }; | ||||
|   | ||||
| @@ -154,13 +173,10 @@ static int devapc_sync_vio_dbg(struct mt | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| -/* | ||||
| - * devapc_extract_vio_dbg - extract full violation information after doing | ||||
| - *                          shift mechanism. | ||||
| - */ | ||||
| -static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx) | ||||
| + | ||||
| +static void devapc_extract_vio_dbg_v1(struct mtk_devapc_context *ctx) | ||||
|  { | ||||
| -	struct mtk_devapc_vio_dbgs vio_dbgs; | ||||
| +	struct mtk_devapc_vio_dbgs_v1 vio_dbgs; | ||||
|  	void __iomem *vio_dbg0_reg; | ||||
|  	void __iomem *vio_dbg1_reg; | ||||
|   | ||||
| @@ -181,6 +197,46 @@ static void devapc_extract_vio_dbg(struc | ||||
|  		 vio_dbgs.vio_dbg1); | ||||
|  } | ||||
|   | ||||
| +static void devapc_extract_vio_dbg_v2(struct mtk_devapc_context *ctx) | ||||
| +{ | ||||
| +	struct mtk_devapc_vio_dbgs_v2 vio_dbgs; | ||||
| +	void __iomem *vio_dbg0_reg; | ||||
| +	void __iomem *vio_dbg1_reg; | ||||
| +	void __iomem *vio_dbg2_reg; | ||||
| +	void __iomem *vio_dbg3_reg; | ||||
| + | ||||
| +	vio_dbg0_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg0_offset; | ||||
| +	vio_dbg1_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg1_offset; | ||||
| +	vio_dbg2_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg2_offset; | ||||
| +	vio_dbg3_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg3_offset; | ||||
| + | ||||
| +	vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg); | ||||
| +	vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg); | ||||
| +	vio_dbgs.vio_dbg2 = readl(vio_dbg2_reg); | ||||
| +	vio_dbgs.vio_dbg3 = readl(vio_dbg3_reg); | ||||
| + | ||||
| +	if (vio_dbgs.dbg0_bits.vio_w) | ||||
| +		dev_info(ctx->dev, "Write Violation\n"); | ||||
| +	else if (vio_dbgs.dbg0_bits.vio_r) | ||||
| +		dev_info(ctx->dev, "Read Violation\n"); | ||||
| + | ||||
| +	dev_info(ctx->dev, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%llx\n", | ||||
| +		 vio_dbgs.vio_dbg1, vio_dbgs.dbg0_bits.dmnid, | ||||
| +		 ((u64)vio_dbgs.vio_dbg3 << 32) | vio_dbgs.vio_dbg2); | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * devapc_extract_vio_dbg - extract full violation information after doing | ||||
| + *                          shift mechanism. | ||||
| + */ | ||||
| +static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx) | ||||
| +{ | ||||
| +	if (ctx->data->version == 1) | ||||
| +		devapc_extract_vio_dbg_v1(ctx); | ||||
| +	else | ||||
| +		devapc_extract_vio_dbg_v2(ctx); | ||||
| +} | ||||
| + | ||||
|  /* | ||||
|   * devapc_violation_irq - the devapc Interrupt Service Routine (ISR) will dump | ||||
|   *                        violation information including which master violates | ||||
| @@ -229,16 +285,37 @@ static const struct mtk_devapc_regs_ofs | ||||
|  	.vio_shift_con_offset = 0xF20, | ||||
|  }; | ||||
|   | ||||
| +static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt7987 = { | ||||
| +	.vio_mask_offset = 0x0, | ||||
| +	.vio_sta_offset = 0x400, | ||||
| +	.vio_dbg0_offset = 0x900, | ||||
| +	.vio_dbg1_offset = 0x904, | ||||
| +	.vio_dbg2_offset = 0x908, | ||||
| +	.vio_dbg3_offset = 0x90C, | ||||
| +	.apc_con_offset = 0xF00, | ||||
| +	.vio_shift_sta_offset = 0xF20, | ||||
| +	.vio_shift_sel_offset = 0xF30, | ||||
| +	.vio_shift_con_offset = 0xF10, | ||||
| +}; | ||||
| + | ||||
|  static const struct mtk_devapc_data devapc_mt6779 = { | ||||
|  	.vio_idx_num = 511, | ||||
| +	.version = 1, | ||||
|  	.regs_ofs = &devapc_regs_ofs_mt6779, | ||||
|  }; | ||||
|   | ||||
|  static const struct mtk_devapc_data devapc_mt8186 = { | ||||
|  	.vio_idx_num = 519, | ||||
| +	.version = 1, | ||||
|  	.regs_ofs = &devapc_regs_ofs_mt6779, | ||||
|  }; | ||||
|   | ||||
| +static const struct mtk_devapc_data devapc_mt7987 = { | ||||
| +	.vio_idx_num = 283, | ||||
| +	.version = 2, | ||||
| +	.regs_ofs = &devapc_regs_ofs_mt7987, | ||||
| +}; | ||||
| + | ||||
|  static const struct of_device_id mtk_devapc_dt_match[] = { | ||||
|  	{ | ||||
|  		.compatible = "mediatek,mt6779-devapc", | ||||
| @@ -247,6 +324,9 @@ static const struct of_device_id mtk_dev | ||||
|  		.compatible = "mediatek,mt8186-devapc", | ||||
|  		.data = &devapc_mt8186, | ||||
|  	}, { | ||||
| +		.compatible = "mediatek,mt7987-devapc", | ||||
| +		.data = &devapc_mt7987, | ||||
| +	}, { | ||||
|  	}, | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, mtk_devapc_dt_match); | ||||
| @@ -276,9 +356,11 @@ static int mtk_devapc_probe(struct platf | ||||
|  	if (!devapc_irq) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	ctx->infra_clk = devm_clk_get_enabled(&pdev->dev, "devapc-infra-clock"); | ||||
| -	if (IS_ERR(ctx->infra_clk)) | ||||
| -		return -EINVAL; | ||||
| +	if (ctx->data->version == 1) { | ||||
| +		ctx->infra_clk = devm_clk_get_enabled(&pdev->dev, "devapc-infra-clock"); | ||||
| +		if (IS_ERR(ctx->infra_clk)) | ||||
| +			return -EINVAL; | ||||
| +	} | ||||
|   | ||||
|  	ret = devm_request_irq(&pdev->dev, devapc_irq, devapc_violation_irq, | ||||
|  			       IRQF_TRIGGER_NONE, "devapc", ctx); | ||||
| @@ -0,0 +1,69 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 15 Mar 2018 20:46:31 +0100 | ||||
| Subject: [PATCH] netfilter: nf_flow_table: support hw offload through | ||||
|  virtual interfaces | ||||
|  | ||||
| There are hardware offload devices that support offloading VLANs and | ||||
| PPPoE devices. Additionally, it is useful to be able to offload packets | ||||
| routed through bridge interfaces as well. | ||||
| Add support for finding the path to the offload device through these | ||||
| virtual interfaces, while collecting useful parameters for the offload | ||||
| device, like VLAN ID/protocol, PPPoE session and Ethernet MAC address. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h | ||||
| index 2d1aa35..b60b506 100644 | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -989,6 +989,7 @@ struct dev_ifalias { | ||||
|  struct devlink; | ||||
|  struct tlsdev_ops; | ||||
|   | ||||
| +struct flow_offload_hw_path; | ||||
|   | ||||
|  /* | ||||
|   * This structure defines the management hooks for network devices. | ||||
| @@ -1222,6 +1223,11 @@ struct tlsdev_ops; | ||||
|   * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh, | ||||
|   *			     u16 flags); | ||||
|   * | ||||
| + * int (*ndo_flow_offload_check)(struct flow_offload_hw_path *path); | ||||
| + *	For virtual devices like bridges, vlan, and pppoe, fill in the | ||||
| + *	underlying network device that can be used for offloading connections. | ||||
| + *	Return an error if offloading is not supported. | ||||
| + * | ||||
|   * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier); | ||||
|   *	Called to change device carrier. Soft-devices (like dummy, team, etc) | ||||
|   *	which do not represent real hardware may define this to allow their | ||||
| @@ -1471,6 +1477,7 @@ struct net_device_ops { | ||||
|  	int			(*ndo_bridge_dellink)(struct net_device *dev, | ||||
|  						      struct nlmsghdr *nlh, | ||||
|  						      u16 flags); | ||||
| +	int			(*ndo_flow_offload_check)(struct flow_offload_hw_path *path); | ||||
|  	int			(*ndo_change_carrier)(struct net_device *dev, | ||||
|  						      bool new_carrier); | ||||
|  	int			(*ndo_get_phys_port_id)(struct net_device *dev, | ||||
| diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h | ||||
| index 7374cb2..e0c0a80 100644 | ||||
| --- a/include/net/netfilter/nf_flow_table.h | ||||
| +++ b/include/net/netfilter/nf_flow_table.h | ||||
| @@ -179,6 +179,17 @@ struct flow_offload { | ||||
|  	struct rcu_head				rcu_head; | ||||
|  }; | ||||
|   | ||||
| +struct flow_offload_hw_path { | ||||
| +	struct net_device *dev; | ||||
| +	u32 flags; | ||||
| + | ||||
| +	u8 eth_src[ETH_ALEN]; | ||||
| +	u8 eth_dest[ETH_ALEN]; | ||||
| +	u16 vlan_proto; | ||||
| +	u16 vlan_id; | ||||
| +	u16 pppoe_sid; | ||||
| +}; | ||||
| + | ||||
|  #define NF_FLOW_TIMEOUT (30 * HZ) | ||||
|  #define nf_flowtable_time_stamp	(u32)jiffies | ||||
|   | ||||
| @@ -0,0 +1,63 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 15 Mar 2018 20:49:58 +0100 | ||||
| Subject: [PATCH] net: 8021q: support hardware flow table offload | ||||
|  | ||||
| Add the VLAN ID and protocol information | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c | ||||
| index c373f1d..f45abd5 100644 | ||||
| --- a/net/8021q/vlan_dev.c | ||||
| +++ b/net/8021q/vlan_dev.c | ||||
| @@ -27,6 +27,11 @@ | ||||
|  #include <linux/phy.h> | ||||
|  #include <net/arp.h> | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
| + | ||||
|  #include "vlan.h" | ||||
|  #include "vlanproc.h" | ||||
|  #include <linux/if_vlan.h> | ||||
| @@ -791,6 +796,27 @@ static int vlan_dev_fill_forward_path(struct net_device_path_ctx *ctx, | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *dev = path->dev; | ||||
| +	struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | ||||
| + | ||||
| +	if (path->flags & BIT(DEV_PATH_VLAN)) | ||||
| +		return -EEXIST; | ||||
| + | ||||
| +	path->flags |= BIT(DEV_PATH_VLAN); | ||||
| +	path->vlan_proto = vlan->vlan_proto; | ||||
| +	path->vlan_id = vlan->vlan_id; | ||||
| +	path->dev = vlan->real_dev; | ||||
| + | ||||
| +	if (vlan->real_dev->netdev_ops->ndo_flow_offload_check) | ||||
| +		return vlan->real_dev->netdev_ops->ndo_flow_offload_check(path); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct ethtool_ops vlan_ethtool_ops = { | ||||
|  	.get_link_ksettings	= vlan_ethtool_get_link_ksettings, | ||||
|  	.get_drvinfo	        = vlan_ethtool_get_drvinfo, | ||||
| @@ -830,6 +856,9 @@ static const struct net_device_ops vlan_netdev_ops = { | ||||
|  	.ndo_fix_features	= vlan_dev_fix_features, | ||||
|  	.ndo_get_iflink		= vlan_dev_get_iflink, | ||||
|  	.ndo_fill_forward_path	= vlan_dev_fill_forward_path, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check = vlan_dev_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static void vlan_dev_free(struct net_device *dev) | ||||
| @@ -0,0 +1,63 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 15 Mar 2018 20:50:37 +0100 | ||||
| Subject: [PATCH] net: bridge: support hardware flow table offload | ||||
|  | ||||
| Look up the real device and pass it on | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c | ||||
| index 03934dd..094b2b1 100644 | ||||
| --- a/net/bridge/br_device.c | ||||
| +++ b/net/bridge/br_device.c | ||||
| @@ -14,6 +14,10 @@ | ||||
|  #include <linux/ethtool.h> | ||||
|  #include <linux/list.h> | ||||
|  #include <linux/netfilter_bridge.h> | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
|   | ||||
|  #include <linux/uaccess.h> | ||||
|  #include "br_private.h" | ||||
| @@ -440,6 +444,28 @@ static const struct ethtool_ops br_ethtool_ops = { | ||||
|  	.get_link	= ethtool_op_get_link, | ||||
|  }; | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int br_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *dev = path->dev; | ||||
| +	struct net_bridge *br = netdev_priv(dev); | ||||
| +	struct net_bridge_fdb_entry *dst; | ||||
| + | ||||
| +	if (!(path->flags & BIT(DEV_PATH_ETHERNET))) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	dst = br_fdb_find_rcu(br, path->eth_dest, path->vlan_id); | ||||
| +	if (!dst || !dst->dst) | ||||
| +		return -ENOENT; | ||||
| + | ||||
| +	path->dev = dst->dst->dev; | ||||
| +	if (path->dev->netdev_ops->ndo_flow_offload_check) | ||||
| +		return path->dev->netdev_ops->ndo_flow_offload_check(path); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct net_device_ops br_netdev_ops = { | ||||
|  	.ndo_open		 = br_dev_open, | ||||
|  	.ndo_stop		 = br_dev_stop, | ||||
| @@ -469,6 +495,9 @@ static const struct net_device_ops br_netdev_ops = { | ||||
|  	.ndo_bridge_dellink	 = br_dellink, | ||||
|  	.ndo_features_check	 = passthru_features_check, | ||||
|  	.ndo_fill_forward_path	 = br_fill_forward_path, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check	 = br_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static struct device_type br_type = { | ||||
| @@ -0,0 +1,132 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 15 Mar 2018 21:15:00 +0100 | ||||
| Subject: [PATCH] net: pppoe: support hardware flow table offload | ||||
|  | ||||
| Pass on the PPPoE session ID and the remote MAC address | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c | ||||
| index ec2fbd1..f361b52 100644 | ||||
| --- a/drivers/net/ppp/ppp_generic.c | ||||
| +++ b/drivers/net/ppp/ppp_generic.c | ||||
| @@ -53,6 +53,11 @@ | ||||
|  #include <net/net_namespace.h> | ||||
|  #include <net/netns/generic.h> | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
| + | ||||
|  #define PPP_VERSION	"2.4.2" | ||||
|   | ||||
|  /* | ||||
| @@ -1416,6 +1421,28 @@ static int ppp_fill_forward_path(struct net_device_path_ctx *ctx, | ||||
|  	return chan->ops->fill_forward_path(ctx, path, chan); | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int ppp_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct ppp *ppp = netdev_priv(path->dev); | ||||
| +	struct ppp_channel *chan; | ||||
| +	struct channel *pch; | ||||
| + | ||||
| +	if (ppp->flags & SC_MULTILINK) | ||||
| +		return -EOPNOTSUPP; | ||||
| + | ||||
| +	if (list_empty(&ppp->channels)) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	pch = list_first_entry(&ppp->channels, struct channel, clist); | ||||
| +	chan = pch->chan; | ||||
| +	if (!chan->ops->flow_offload_check) | ||||
| +		return -EOPNOTSUPP; | ||||
| + | ||||
| +	return chan->ops->flow_offload_check(chan, path); | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct net_device_ops ppp_netdev_ops = { | ||||
|  	.ndo_init	 = ppp_dev_init, | ||||
|  	.ndo_uninit      = ppp_dev_uninit, | ||||
| @@ -1423,6 +1450,9 @@ static const struct net_device_ops ppp_netdev_ops = { | ||||
|  	.ndo_do_ioctl    = ppp_net_ioctl, | ||||
|  	.ndo_get_stats64 = ppp_get_stats64, | ||||
|  	.ndo_fill_forward_path = ppp_fill_forward_path, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check = ppp_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static struct device_type ppp_type = { | ||||
| diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c | ||||
| index 7a8c246..1018464 100644 | ||||
| --- a/drivers/net/ppp/pppoe.c | ||||
| +++ b/drivers/net/ppp/pppoe.c | ||||
| @@ -73,6 +73,11 @@ | ||||
|  #include <linux/proc_fs.h> | ||||
|  #include <linux/seq_file.h> | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
| + | ||||
|  #include <linux/nsproxy.h> | ||||
|  #include <net/net_namespace.h> | ||||
|  #include <net/netns/generic.h> | ||||
| @@ -997,9 +1002,37 @@ static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx, | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int pppoe_flow_offload_check(struct ppp_channel *chan, | ||||
| +				    struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct sock *sk = (struct sock *)chan->private; | ||||
| +	struct pppox_sock *po = pppox_sk(sk); | ||||
| +	struct net_device *dev = po->pppoe_dev; | ||||
| + | ||||
| +	if (sock_flag(sk, SOCK_DEAD) || | ||||
| +	    !(sk->sk_state & PPPOX_CONNECTED) || !dev) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	path->dev = po->pppoe_dev; | ||||
| +	path->flags |= BIT(DEV_PATH_PPPOE); | ||||
| +	memcpy(path->eth_src, po->pppoe_dev->dev_addr, ETH_ALEN); | ||||
| +	memcpy(path->eth_dest, po->pppoe_pa.remote, ETH_ALEN); | ||||
| +	path->pppoe_sid = be16_to_cpu(po->num); | ||||
| + | ||||
| +	if (path->dev->netdev_ops->ndo_flow_offload_check) | ||||
| +		return path->dev->netdev_ops->ndo_flow_offload_check(path); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct ppp_channel_ops pppoe_chan_ops = { | ||||
|  	.start_xmit = pppoe_xmit, | ||||
|  	.fill_forward_path = pppoe_fill_forward_path, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.flow_offload_check = pppoe_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, | ||||
| diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h | ||||
| index 91f9a92..4a1729b 100644 | ||||
| --- a/include/linux/ppp_channel.h | ||||
| +++ b/include/linux/ppp_channel.h | ||||
| @@ -31,6 +31,9 @@ struct ppp_channel_ops { | ||||
|  	int	(*fill_forward_path)(struct net_device_path_ctx *, | ||||
|  				     struct net_device_path *, | ||||
|  				     const struct ppp_channel *); | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	int	(*flow_offload_check)(struct ppp_channel *, struct flow_offload_hw_path *); | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  struct ppp_channel { | ||||
| @@ -0,0 +1,74 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 17 Sep 2020 18:41:23 +0200 | ||||
| Subject: [PATCH] net: dsa: support hardware flow table offload | ||||
|  | ||||
| Look up the master device and the port id | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h | ||||
| index e0c0a80..a1b4ab5 100644 | ||||
| --- a/include/net/netfilter/nf_flow_table.h | ||||
| +++ b/include/net/netfilter/nf_flow_table.h | ||||
| @@ -188,6 +188,7 @@ struct flow_offload_hw_path { | ||||
|  	u16 vlan_proto; | ||||
|  	u16 vlan_id; | ||||
|  	u16 pppoe_sid; | ||||
| +	u16 dsa_port; | ||||
|  }; | ||||
|   | ||||
|  #define NF_FLOW_TIMEOUT (30 * HZ) | ||||
| diff --git a/net/dsa/slave.c b/net/dsa/slave.c | ||||
| index 2ea9ec1..eab4e4a 100644 | ||||
| --- a/net/dsa/slave.c | ||||
| +++ b/net/dsa/slave.c | ||||
| @@ -19,6 +19,10 @@ | ||||
|  #include <linux/if_bridge.h> | ||||
|  #include <linux/netpoll.h> | ||||
|  #include <linux/ptp_classify.h> | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
|   | ||||
|  #include "dsa_priv.h" | ||||
|   | ||||
| @@ -1257,6 +1261,27 @@ static int dsa_slave_fill_forward_path(struct net_device_path_ctx *ctx, | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int dsa_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *dev = path->dev; | ||||
| +	struct dsa_port *dp; | ||||
| + | ||||
| +	if (!(path->flags & BIT(DEV_PATH_ETHERNET))) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	dp = dsa_slave_to_port(dev); | ||||
| +	path->dsa_port = dp->index; | ||||
| +	path->dev = dsa_slave_to_master(dev); | ||||
| +	path->flags |= BIT(DEV_PATH_DSA); | ||||
| + | ||||
| +	if (path->dev->netdev_ops->ndo_flow_offload_check) | ||||
| +		return path->dev->netdev_ops->ndo_flow_offload_check(path); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct net_device_ops dsa_slave_netdev_ops = { | ||||
|  	.ndo_open	 	= dsa_slave_open, | ||||
|  	.ndo_stop		= dsa_slave_close, | ||||
| @@ -1282,6 +1307,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = { | ||||
|  	.ndo_vlan_rx_kill_vid	= dsa_slave_vlan_rx_kill_vid, | ||||
|  	.ndo_get_devlink_port	= dsa_slave_get_devlink_port, | ||||
|  	.ndo_fill_forward_path	= dsa_slave_fill_forward_path, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check	 = dsa_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static struct device_type dsa_type = { | ||||
| @@ -9,11 +9,9 @@ Subject: [PATCH] | ||||
|  drivers/crypto/inside-secure/safexcel.h | 15 ++++++ | ||||
|  2 files changed, 78 insertions(+), 6 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c | ||||
| index 647c5a0c1..6f4fc15b7 100644 | ||||
| --- a/drivers/crypto/inside-secure/safexcel.c | ||||
| +++ b/drivers/crypto/inside-secure/safexcel.c | ||||
| @@ -304,6 +304,11 @@ static void eip197_init_firmware(struct safexcel_crypto_priv *priv) | ||||
| @@ -304,6 +304,11 @@ static void eip197_init_firmware(struct | ||||
|  		/* Enable access to all IFPP program memories */ | ||||
|  		writel(EIP197_PE_ICE_RAM_CTRL_FPP_PROG_EN, | ||||
|  		       EIP197_PE(priv) + EIP197_PE_ICE_RAM_CTRL(pe)); | ||||
| @@ -25,7 +23,7 @@ index 647c5a0c1..6f4fc15b7 100644 | ||||
|  	} | ||||
|   | ||||
|  } | ||||
| @@ -403,13 +408,13 @@ static int eip197_load_firmwares(struct safexcel_crypto_priv *priv) | ||||
| @@ -403,13 +408,13 @@ static int eip197_load_firmwares(struct | ||||
|  	const struct firmware *fw[FW_NB]; | ||||
|  	char fw_path[37], *dir = NULL; | ||||
|  	int i, j, ret = 0, pe; | ||||
| @@ -51,7 +49,7 @@ index 647c5a0c1..6f4fc15b7 100644 | ||||
|  	if (eip197_start_firmware(priv, ipuesz, ifppsz, minifw)) { | ||||
|  		dev_dbg(priv->dev, "Firmware loaded successfully\n"); | ||||
|  		return 0; | ||||
| @@ -592,6 +600,11 @@ static int safexcel_hw_init(struct safexcel_crypto_priv *priv) | ||||
| @@ -592,6 +600,11 @@ static int safexcel_hw_init(struct safex | ||||
|  	 */ | ||||
|  	if (priv->flags & SAFEXCEL_HW_EIP197) { | ||||
|  		val = readl(EIP197_HIA_AIC(priv) + EIP197_HIA_MST_CTRL); | ||||
| @@ -63,7 +61,7 @@ index 647c5a0c1..6f4fc15b7 100644 | ||||
|  		val |= EIP197_MST_CTRL_TX_MAX_CMD(5); | ||||
|  		writel(val, EIP197_HIA_AIC(priv) + EIP197_HIA_MST_CTRL); | ||||
|  	} | ||||
| @@ -792,6 +805,12 @@ static int safexcel_hw_init(struct safexcel_crypto_priv *priv) | ||||
| @@ -792,6 +805,12 @@ static int safexcel_hw_init(struct safex | ||||
|  			return ret; | ||||
|  	} | ||||
|   | ||||
| @@ -76,7 +74,7 @@ index 647c5a0c1..6f4fc15b7 100644 | ||||
|  	return safexcel_hw_setup_cdesc_rings(priv) ?: | ||||
|  	       safexcel_hw_setup_rdesc_rings(priv) ?: | ||||
|  	       0; | ||||
| @@ -1498,6 +1517,9 @@ static int safexcel_probe_generic(void *pdev, | ||||
| @@ -1503,6 +1522,9 @@ static int safexcel_probe_generic(void * | ||||
|  	hwopt = readl(EIP197_GLOBAL(priv) + EIP197_OPTIONS); | ||||
|  	hiaopt = readl(EIP197_HIA_AIC(priv) + EIP197_HIA_OPTIONS); | ||||
|   | ||||
| @@ -86,7 +84,7 @@ index 647c5a0c1..6f4fc15b7 100644 | ||||
|  	if (priv->flags & SAFEXCEL_HW_EIP197) { | ||||
|  		/* EIP197 */ | ||||
|  		peopt = readl(EIP197_PE(priv) + EIP197_PE_OPTIONS(0)); | ||||
| @@ -1516,8 +1538,37 @@ static int safexcel_probe_generic(void *pdev, | ||||
| @@ -1521,8 +1543,37 @@ static int safexcel_probe_generic(void * | ||||
|  					    EIP197_N_RINGS_MASK; | ||||
|  		if (hiaopt & EIP197_HIA_OPT_HAS_PE_ARB) | ||||
|  			priv->flags |= EIP197_PE_ARB; | ||||
| @@ -125,7 +123,7 @@ index 647c5a0c1..6f4fc15b7 100644 | ||||
|  		/* If not a full TRC, then assume simple TRC */ | ||||
|  		if (!(hwopt & EIP197_OPT_HAS_TRC)) | ||||
|  			priv->flags |= EIP197_SIMPLE_TRC; | ||||
| @@ -1555,13 +1606,14 @@ static int safexcel_probe_generic(void *pdev, | ||||
| @@ -1560,13 +1611,14 @@ static int safexcel_probe_generic(void * | ||||
|  				    EIP197_PE_EIP96_OPTIONS(0)); | ||||
|   | ||||
|  	/* Print single info line describing what we just detected */ | ||||
| @@ -142,29 +140,6 @@ index 647c5a0c1..6f4fc15b7 100644 | ||||
|   | ||||
|  	safexcel_configure(priv); | ||||
|   | ||||
| @@ -1690,6 +1742,7 @@ static int safexcel_probe(struct platform_device *pdev) | ||||
|  { | ||||
|  	struct device *dev = &pdev->dev; | ||||
|  	struct safexcel_crypto_priv *priv; | ||||
| +	struct resource *res; | ||||
|  	int ret; | ||||
|   | ||||
|  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||||
| @@ -1701,7 +1754,11 @@ static int safexcel_probe(struct platform_device *pdev) | ||||
|   | ||||
|  	platform_set_drvdata(pdev, priv); | ||||
|   | ||||
| -	priv->base = devm_platform_ioremap_resource(pdev, 0); | ||||
| +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| +	if (!res) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	priv->base = devm_ioremap(dev, res->start, resource_size(res)); | ||||
|  	if (IS_ERR(priv->base)) { | ||||
|  		dev_err(dev, "failed to get resource\n"); | ||||
|  		return PTR_ERR(priv->base); | ||||
| diff --git a/drivers/crypto/inside-secure/safexcel.h b/drivers/crypto/inside-secure/safexcel.h | ||||
| index c031c197e..e9909c336 100644 | ||||
| --- a/drivers/crypto/inside-secure/safexcel.h | ||||
| +++ b/drivers/crypto/inside-secure/safexcel.h | ||||
| @@ -22,6 +22,7 @@ | ||||
| @@ -217,7 +192,7 @@ index c031c197e..e9909c336 100644 | ||||
|  /* EIP197_STRC_CONFIG */ | ||||
|  #define EIP197_STRC_CONFIG_INIT			BIT(31) | ||||
|  #define EIP197_STRC_CONFIG_LARGE_REC(s)		(s<<8) | ||||
| @@ -777,6 +788,7 @@ enum safexcel_flags { | ||||
| @@ -780,6 +791,7 @@ enum safexcel_flags { | ||||
|  	EIP197_PE_ARB		= BIT(2), | ||||
|  	EIP197_ICE		= BIT(3), | ||||
|  	EIP197_SIMPLE_TRC	= BIT(4), | ||||
| @@ -225,7 +200,7 @@ index c031c197e..e9909c336 100644 | ||||
|  }; | ||||
|   | ||||
|  struct safexcel_hwconfig { | ||||
| @@ -784,7 +796,10 @@ struct safexcel_hwconfig { | ||||
| @@ -787,7 +799,10 @@ struct safexcel_hwconfig { | ||||
|  	int hwver; | ||||
|  	int hiaver; | ||||
|  	int ppver; | ||||
| @@ -236,6 +211,3 @@ index c031c197e..e9909c336 100644 | ||||
|  	int hwdataw; | ||||
|  	int hwcfsize; | ||||
|  	int hwrfsize; | ||||
| --  | ||||
| 2.34.1 | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,152 @@ | ||||
| From fd6e50fdeb1d943b889a5aa093790a798ae598d3 Mon Sep 17 00:00:00 2001 | ||||
| From: Sam Shih <sam.shih@mediatek.com> | ||||
| Date: Fri, 2 Jun 2023 13:06:29 +0800 | ||||
| Subject: [PATCH]  | ||||
|  [networking][999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch] | ||||
|  | ||||
| --- | ||||
|  include/linux/netdevice.h             |  1 + | ||||
|  include/net/netfilter/nf_flow_table.h |  1 + | ||||
|  net/8021q/vlan_dev.c                  |  1 + | ||||
|  net/ipv6/ip6_tunnel.c                 | 24 ++++++++++++++++++++++++ | ||||
|  net/ipv6/sit.c                        | 24 ++++++++++++++++++++++++ | ||||
|  5 files changed, 51 insertions(+) | ||||
|  | ||||
| diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h | ||||
| index b60b506..c30952f 100644 | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -849,6 +849,8 @@ enum net_device_path_type { | ||||
|  	DEV_PATH_BRIDGE, | ||||
|  	DEV_PATH_PPPOE, | ||||
|  	DEV_PATH_DSA, | ||||
| +	DEV_PATH_DSLITE, | ||||
| +	DEV_PATH_6RD, | ||||
|  }; | ||||
|   | ||||
|  struct net_device_path { | ||||
| diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h | ||||
| index a1b4ab5..7a2945e 100644 | ||||
| --- a/include/net/netfilter/nf_flow_table.h | ||||
| +++ b/include/net/netfilter/nf_flow_table.h | ||||
| @@ -181,6 +181,7 @@ struct flow_offload { | ||||
|   | ||||
|  struct flow_offload_hw_path { | ||||
|  	struct net_device *dev; | ||||
| +	struct net_device *virt_dev; | ||||
|  	u32 flags; | ||||
|   | ||||
|  	u8 eth_src[ETH_ALEN]; | ||||
| diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c | ||||
| index f45abd5..db5867d 100644 | ||||
| --- a/net/8021q/vlan_dev.c | ||||
| +++ b/net/8021q/vlan_dev.c | ||||
| @@ -808,6 +808,7 @@ static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path) | ||||
|  	path->flags |= BIT(DEV_PATH_VLAN); | ||||
|  	path->vlan_proto = vlan->vlan_proto; | ||||
|  	path->vlan_id = vlan->vlan_id; | ||||
| +	path->virt_dev = dev; | ||||
|  	path->dev = vlan->real_dev; | ||||
|   | ||||
|  	if (vlan->real_dev->netdev_ops->ndo_flow_offload_check) | ||||
| diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c | ||||
| index 2e8a528..0bf25b0 100644 | ||||
| --- a/net/ipv6/ip6_tunnel.c | ||||
| +++ b/net/ipv6/ip6_tunnel.c | ||||
| @@ -57,6 +57,11 @@ | ||||
|  #include <net/netns/generic.h> | ||||
|  #include <net/dst_metadata.h> | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
| + | ||||
|  MODULE_AUTHOR("Ville Nuorvala"); | ||||
|  MODULE_DESCRIPTION("IPv6 tunneling device"); | ||||
|  MODULE_LICENSE("GPL"); | ||||
| @@ -1889,6 +1894,22 @@ int ip6_tnl_get_iflink(const struct net_device *dev) | ||||
|  } | ||||
|  EXPORT_SYMBOL(ip6_tnl_get_iflink); | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int ipip6_dev_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *dev = path->dev; | ||||
| +	struct ip6_tnl *tnl = netdev_priv(dev); | ||||
| + | ||||
| +	if (path->flags & BIT(DEV_PATH_DSLITE)) | ||||
| +		return -EEXIST; | ||||
| + | ||||
| +	path->flags |= BIT(DEV_PATH_DSLITE); | ||||
| +	path->dev = tnl->dev; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  int ip6_tnl_encap_add_ops(const struct ip6_tnl_encap_ops *ops, | ||||
|  			  unsigned int num) | ||||
|  { | ||||
| @@ -1950,6 +1971,9 @@ static const struct net_device_ops ip6_tnl_netdev_ops = { | ||||
|  	.ndo_change_mtu = ip6_tnl_change_mtu, | ||||
|  	.ndo_get_stats	= ip6_get_stats, | ||||
|  	.ndo_get_iflink = ip6_tnl_get_iflink, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check = ipip6_dev_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  #define IPXIPX_FEATURES (NETIF_F_SG |		\ | ||||
| diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c | ||||
| index 8d704ea..c50bbef 100644 | ||||
| --- a/net/ipv6/sit.c | ||||
| +++ b/net/ipv6/sit.c | ||||
| @@ -52,6 +52,11 @@ | ||||
|  #include <net/net_namespace.h> | ||||
|  #include <net/netns/generic.h> | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
| + | ||||
|  /* | ||||
|     This version of net/ipv6/sit.c is cloned of net/ipv4/ip_gre.c | ||||
|   | ||||
| @@ -1344,6 +1349,22 @@ ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int ipip6_dev_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *dev = path->dev; | ||||
| +	struct ip_tunnel *tnl = netdev_priv(dev); | ||||
| + | ||||
| +	if (path->flags & BIT(DEV_PATH_6RD)) | ||||
| +		return -EEXIST; | ||||
| + | ||||
| +	path->flags |= BIT(DEV_PATH_6RD); | ||||
| +	path->dev = tnl->dev; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct net_device_ops ipip6_netdev_ops = { | ||||
|  	.ndo_init	= ipip6_tunnel_init, | ||||
|  	.ndo_uninit	= ipip6_tunnel_uninit, | ||||
| @@ -1351,6 +1372,9 @@ static const struct net_device_ops ipip6_netdev_ops = { | ||||
|  	.ndo_do_ioctl	= ipip6_tunnel_ioctl, | ||||
|  	.ndo_get_stats64 = ip_tunnel_get_stats64, | ||||
|  	.ndo_get_iflink = ip_tunnel_get_iflink, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check = ipip6_dev_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static void ipip6_dev_free(struct net_device *dev) | ||||
| --  | ||||
| 2.34.1 | ||||
|  | ||||
| @@ -0,0 +1,44 @@ | ||||
| --- a/drivers/net/phy/Kconfig | ||||
| +++ b/drivers/net/phy/Kconfig | ||||
| @@ -592,27 +592,7 @@ config MESON_GXL_PHY | ||||
|  	---help--- | ||||
|  	  Currently has a driver for the Amlogic Meson GXL Internal PHY | ||||
|   | ||||
| -config MEDIATEK_GE_PHY | ||||
| -	tristate "MediaTek Gigabit Ethernet PHYs" | ||||
| -	help | ||||
| -	  Supports the MediaTek Gigabit Ethernet PHYs. | ||||
| - | ||||
| -config MEDIATEK_GE_SOC_PHY | ||||
| -	bool "MediaTek SoC Ethernet PHYs" | ||||
| -	depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST | ||||
| -	select NVMEM_MTK_EFUSE | ||||
| -	help | ||||
| -	  Supports MediaTek SoC built-in Gigabit Ethernet PHYs. | ||||
| - | ||||
| -	  Include support for built-in Ethernet PHYs which are present in | ||||
| -	  the MT7981 and MT7988 SoCs. These PHYs need calibration data | ||||
| -	  present in the SoCs efuse and will dynamically calibrate VCM | ||||
| -	  (common-mode voltage) during startup. | ||||
| - | ||||
| -config MEDIATEK_2P5GE_PHY | ||||
| -	tristate "MediaTek 2.5Gb Ethernet PHYs" | ||||
| -	---help--- | ||||
| -	  Supports MediaTek internal 2.5Gb Ethernet PHYs. | ||||
| +source "drivers/net/phy/mediatek/Kconfig" | ||||
|   | ||||
|  config MICREL_PHY | ||||
|  	tristate "Micrel PHYs" | ||||
| --- a/drivers/net/phy/Makefile | ||||
| +++ b/drivers/net/phy/Makefile | ||||
| @@ -105,9 +105,7 @@ obj-$(CONFIG_LXT_PHY)		+= lxt.o | ||||
|  obj-$(CONFIG_MARVELL_PHY)	+= marvell.o | ||||
|  obj-$(CONFIG_MARVELL_10G_PHY)	+= marvell10g.o | ||||
|  obj-$(CONFIG_MAXLINEAR_GPHY)	+= mxl-gpy.o | ||||
| -obj-$(CONFIG_MEDIATEK_GE_PHY)	+= mediatek-ge.o | ||||
| -obj-$(CONFIG_MEDIATEK_GE_SOC_PHY)	+= mediatek-ge-soc.o | ||||
| -obj-$(CONFIG_MEDIATEK_2P5GE_PHY)+= mediatek-2p5ge.o | ||||
| +obj-y				+= mediatek/ | ||||
|  obj-$(CONFIG_MESON_GXL_PHY)	+= meson-gxl.o | ||||
|  obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o | ||||
|  obj-$(CONFIG_MICREL_PHY)	+= micrel.o | ||||
| @@ -153,7 +153,7 @@ new file mode 100644 | ||||
| index 000000000..d2828aad4 | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/phy/aquantia_firmware.c | ||||
| @@ -0,0 +1,1109 @@ | ||||
| @@ -0,0 +1,1110 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0 | ||||
| +/* FW download driver for Aquantia PHY | ||||
| + */ | ||||
| @@ -1227,7 +1227,6 @@ index 000000000..d2828aad4 | ||||
| +				 PTR_ERR(gangload_kthread)); | ||||
| +			return PTR_ERR(gangload_kthread); | ||||
| +		} | ||||
| +		wake_up_process(gangload_kthread); | ||||
| +	} | ||||
| + | ||||
| +	for (i = 0; i < gangload; i++) { | ||||
| @@ -1250,6 +1249,8 @@ index 000000000..d2828aad4 | ||||
| +	gangload_phydevs[gangload] = phydev; | ||||
| +	gangload++; | ||||
| + | ||||
| +	wake_up_process(gangload_kthread); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|   | ||||
| @@ -0,0 +1,66 @@ | ||||
| diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h | ||||
| index c30952f..6c0860b 100644 | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -851,6 +851,7 @@ enum net_device_path_type { | ||||
|  	DEV_PATH_DSA, | ||||
|  	DEV_PATH_DSLITE, | ||||
|  	DEV_PATH_6RD, | ||||
| +	DEV_PATH_TNL, | ||||
|  }; | ||||
|   | ||||
|  struct net_device_path { | ||||
| diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c | ||||
| index 7d3c782..d7a5a9a 100644 | ||||
| --- a/net/l2tp/l2tp_ppp.c | ||||
| +++ b/net/l2tp/l2tp_ppp.c | ||||
| @@ -89,6 +89,7 @@ | ||||
|  #include <linux/nsproxy.h> | ||||
|  #include <net/net_namespace.h> | ||||
|  #include <net/netns/generic.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
|  #include <net/ip.h> | ||||
|  #include <net/udp.h> | ||||
|  #include <net/inet_common.h> | ||||
| @@ -124,9 +125,14 @@ struct pppol2tp_session { | ||||
|  }; | ||||
|   | ||||
|  static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb); | ||||
| +static int l2tp_ppp_flow_offload_check(struct ppp_channel *chan, | ||||
| +				       struct flow_offload_hw_path *path); | ||||
|   | ||||
|  static const struct ppp_channel_ops pppol2tp_chan_ops = { | ||||
|  	.start_xmit =  pppol2tp_xmit, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.flow_offload_check = l2tp_ppp_flow_offload_check, | ||||
| +#endif /* IS_ENABLED(CONFIG_NF_FLOW_TABLE) */ | ||||
|  }; | ||||
|   | ||||
|  static const struct proto_ops pppol2tp_ops; | ||||
| @@ -335,6 +341,26 @@ static int pppol2tp_sendmsg(struct socket *sock, struct msghdr *m, | ||||
|  	return error; | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int l2tp_ppp_flow_offload_check(struct ppp_channel *chan, | ||||
| +				       struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct sock *sk = (struct sock *)chan->private; | ||||
| +	struct l2tp_session *session; | ||||
| + | ||||
| +	if (path->flags & BIT(DEV_PATH_TNL)) | ||||
| +		return -EEXIST; | ||||
| + | ||||
| +	session = pppol2tp_sock_to_session(sk); | ||||
| +	if (!session) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	path->flags |= BIT(DEV_PATH_TNL); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* IS_ENABLED(CONFIG_NF_FLOW_TABLE) */ | ||||
| + | ||||
|  /* Transmit function called by generic PPP driver.  Sends PPP frame | ||||
|   * over PPPoL2TP socket. | ||||
|   * | ||||
| @@ -6,9 +6,9 @@ Subject: [PATCH] 999-2727-net-phy-sfp-add-debug-info.patch | ||||
| --- | ||||
|  drivers/net/phy/phylink.c     | 11 +++++++- | ||||
|  drivers/net/phy/sfp-bus.c     |  3 +++ | ||||
|  drivers/net/phy/sfp.c         | 51 +++++++++++++++++++++++++++++------ | ||||
|  drivers/net/phy/sfp.c         | 50 +++++++++++++++++++++++++++++------ | ||||
|  include/linux/mdio/mdio-i2c.h | 16 +++++++++++ | ||||
|  4 files changed, 72 insertions(+), 9 deletions(-) | ||||
|  4 files changed, 71 insertions(+), 9 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c | ||||
| index 949e3b8..bb4cd28 100644 | ||||
| @@ -141,11 +141,10 @@ index 0fdf5d6..0c335b1 100644 | ||||
|  	err = sfp_add_phy(sfp->sfp_bus, phy); | ||||
|  	if (err) { | ||||
|  		phy_device_remove(phy); | ||||
| @@ -1779,6 +1810,10 @@ static int sfp_sm_add_mdio_bus(struct sfp *sfp) | ||||
| @@ -1779,6 +1810,9 @@ static int sfp_sm_add_mdio_bus(struct sfp *sfp) | ||||
|  static int sfp_sm_probe_for_phy(struct sfp *sfp) | ||||
|  { | ||||
|  	int err = 0; | ||||
| +	struct phy_device *phy; | ||||
| + | ||||
| +	dev_info(sfp->dev, "probing phy device through the [%s] protocol\n", | ||||
| +	         mdio_i2c_proto_type(sfp->mdio_protocol)); | ||||
|   | ||||
| @@ -0,0 +1,88 @@ | ||||
| --- a/net/xfrm/xfrm_output.c | ||||
| +++ b/net/xfrm/xfrm_output.c | ||||
| @@ -410,7 +410,7 @@ static int xfrm_output_one(struct sk_buf | ||||
|  	struct xfrm_state *x = dst->xfrm; | ||||
|  	struct net *net = xs_net(x); | ||||
|   | ||||
| -	if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET) | ||||
| +	if (err <= 0) | ||||
|  		goto resume; | ||||
|   | ||||
|  	do { | ||||
| @@ -570,12 +570,10 @@ int xfrm_output(struct sock *sk, struct | ||||
|   | ||||
|  	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) { | ||||
|  		if (!xfrm_dev_offload_ok(skb, x)) { | ||||
| -			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); | ||||
| -			kfree_skb(skb); | ||||
| -			return -EHOSTUNREACH; | ||||
| +			secpath_reset(skb); | ||||
| +			goto sw_path; | ||||
|  		} | ||||
| - | ||||
| -		return xfrm_output_resume(skb, 0); | ||||
| +		return 0; | ||||
|  	} | ||||
|   | ||||
|  	secpath_reset(skb); | ||||
| @@ -606,6 +604,7 @@ int xfrm_output(struct sock *sk, struct | ||||
|  		if (x->xso.dev && x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM) | ||||
|  			goto out; | ||||
|  	} else { | ||||
| +sw_path: | ||||
|  		if (skb_is_gso(skb)) | ||||
|  			return xfrm_output_gso(net, sk, skb); | ||||
|  	} | ||||
| --- a/net/xfrm/xfrm_policy.c | ||||
| +++ b/net/xfrm/xfrm_policy.c | ||||
| @@ -3701,6 +3701,10 @@ int __xfrm_policy_check(struct sock *sk, | ||||
|  		} | ||||
|  	} | ||||
|  #endif | ||||
| +	/* Inbound HW offload packets, pass the check directly */ | ||||
| +	if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET && | ||||
| +	    (pol->xdo.dir == XFRM_DEV_OFFLOAD_IN || pol->xdo.dir == XFRM_DEV_OFFLOAD_FWD)) | ||||
| +		return 1; | ||||
|   | ||||
|  	if (pol->action == XFRM_POLICY_ALLOW) { | ||||
|  		static struct sec_path dummy; | ||||
| @@ -3710,6 +3714,14 @@ int __xfrm_policy_check(struct sock *sk, | ||||
|  		int ti = 0; | ||||
|  		int i, k; | ||||
|   | ||||
| +		/* Strongswan install FWD policy for inbound HW offload | ||||
| +		 * packets. But cannot find corresponding packet offload | ||||
| +		 * state here and will be drop. So, we bypass following | ||||
| +		 * check for FWD policy with acction allow. | ||||
| +		 */ | ||||
| +		if (dir == XFRM_POLICY_FWD) | ||||
| +			return 1; | ||||
| + | ||||
|  		sp = skb_sec_path(skb); | ||||
|  		if (!sp) | ||||
|  			sp = &dummy; | ||||
| --- a/net/xfrm/xfrm_device.c | ||||
| +++ b/net/xfrm/xfrm_device.c | ||||
| @@ -215,8 +215,8 @@ int xfrm_dev_state_add(struct net *net, | ||||
|  	if (!x->type_offload) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	/* We don't yet support UDP encapsulation and TFC padding. */ | ||||
| -	if (x->encap || x->tfcpad) | ||||
| +	/* We don't yet support TFC padding. */ | ||||
| +	if (x->tfcpad) | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	if (xuo->flags & | ||||
| --- a/net/8021q/vlan_dev.c | ||||
| +++ b/net/8021q/vlan_dev.c | ||||
| @@ -653,6 +653,9 @@ static netdev_features_t vlan_dev_fix_fe | ||||
|  	features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE); | ||||
|  	features |= NETIF_F_LLTX; | ||||
|   | ||||
| +	if (old_features & NETIF_F_HW_ESP) | ||||
| +		features |= NETIF_F_HW_ESP; | ||||
| + | ||||
|  	return features; | ||||
|  } | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| --- a/drivers/net/phy/phylink.c | ||||
| +++ b/drivers/net/phy/phylink.c | ||||
| @@ -686,7 +686,10 @@ static void phylink_link_up(struct phyli | ||||
|  		 * the link_state) to the interface speed, and will send | ||||
|  		 * pause frames to the MAC to limit its transmission speed. | ||||
|  		 */ | ||||
| -		speed = phylink_interface_max_speed(link_state.interface); | ||||
| +		/* For tunnel HW offload, we need to get true link rate to | ||||
| +		 * set QDMA rate limit as link rate. | ||||
| +		 */ | ||||
| +		// speed = phylink_interface_max_speed(link_state.interface); | ||||
|  		duplex = DUPLEX_FULL; | ||||
|  		rx_pause = true; | ||||
|  		break; | ||||
| @@ -1,16 +1,4 @@ | ||||
| From 90508a46a0fd6416dcaad2c7f0ef25a5a421bf4f Mon Sep 17 00:00:00 2001 | ||||
| From: Sam Shih <sam.shih@mediatek.com> | ||||
| Date: Fri, 2 Jun 2023 13:06:00 +0800 | ||||
| Subject: [PATCH]  | ||||
|  [backport-networking-drivers][999-1706-net-dsa-support-mt7988.patch] | ||||
|  | ||||
| --- | ||||
|  drivers/net/dsa/mt7530.c | 191 ++++++++++++++++++++++++++++++++------- | ||||
|  drivers/net/dsa/mt7530.h |  11 ++- | ||||
|  2 files changed, 164 insertions(+), 38 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c | ||||
| index 63f8a632b..2cd5dae9c 100644 | ||||
| =================================================================== | ||||
| --- a/drivers/net/dsa/mt7530.c | ||||
| +++ b/drivers/net/dsa/mt7530.c | ||||
| @@ -19,6 +19,7 @@ | ||||
| @@ -21,7 +9,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|   | ||||
|  #include "mt7530.h" | ||||
|  #include "mt7530_nl.h" | ||||
| @@ -170,28 +171,44 @@ core_clear(struct mt7530_priv *priv, u32 reg, u32 val) | ||||
| @@ -170,28 +171,44 @@ core_clear(struct mt7530_priv *priv, u32 | ||||
|  	core_rmw(priv, reg, val, 0); | ||||
|  } | ||||
|   | ||||
| @@ -43,12 +31,21 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  	struct mii_bus *bus = priv->bus; | ||||
|  	u16 page, r, lo, hi; | ||||
| -	int ret; | ||||
| +	int ret = 0; | ||||
|   | ||||
| - | ||||
| -	page = (reg >> 6) & 0x3ff; | ||||
| -	r  = (reg >> 2) & 0xf; | ||||
| -	lo = val & 0xffff; | ||||
| -	hi = val >> 16; | ||||
| - | ||||
| -	/* MT7530 uses 31 as the pseudo port */ | ||||
| -	ret = bus->write(bus, 0x1f, 0x1f, page); | ||||
| -	if (ret < 0) | ||||
| -		goto err; | ||||
| +	int ret = 0; | ||||
|   | ||||
| -	ret = bus->write(bus, 0x1f, r,  lo); | ||||
| -	if (ret < 0) | ||||
| -		goto err; | ||||
| +	if (priv->direct_access){ | ||||
| +		mtk_w32(priv, val, reg); | ||||
| +	} else { | ||||
| @@ -56,19 +53,12 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
| +		r  = (reg >> 2) & 0xf; | ||||
| +		lo = val & 0xffff; | ||||
| +		hi = val >> 16; | ||||
|   | ||||
| -	/* MT7530 uses 31 as the pseudo port */ | ||||
| -	ret = bus->write(bus, 0x1f, 0x1f, page); | ||||
| -	if (ret < 0) | ||||
| -		goto err; | ||||
| + | ||||
| +		/* MT7530 uses 31 as the pseudo port */ | ||||
| +		ret = bus->write(bus, 0x1f, 0x1f, page); | ||||
| +		if (ret < 0) | ||||
| +			goto err; | ||||
|   | ||||
| -	ret = bus->write(bus, 0x1f, r,  lo); | ||||
| -	if (ret < 0) | ||||
| -		goto err; | ||||
| + | ||||
| +		ret = bus->write(bus, 0x1f, r,  lo); | ||||
| +		if (ret < 0) | ||||
| +			goto err; | ||||
| @@ -79,7 +69,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  err: | ||||
|  	if (ret < 0) | ||||
|  		dev_err(&bus->dev, | ||||
| @@ -206,21 +223,25 @@ mt7530_mii_read(struct mt7530_priv *priv, u32 reg) | ||||
| @@ -206,21 +223,25 @@ mt7530_mii_read(struct mt7530_priv *priv | ||||
|  	u16 page, r, lo, hi; | ||||
|  	int ret; | ||||
|   | ||||
| @@ -117,7 +107,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  } | ||||
|   | ||||
|  void | ||||
| @@ -1907,9 +1928,9 @@ mt7531_phy_supported(struct dsa_switch *ds, int port, | ||||
| @@ -1907,9 +1928,9 @@ mt7531_phy_supported(struct dsa_switch * | ||||
|  		if (mt7531_is_rgmii_port(priv, port)) | ||||
|  			return phy_interface_mode_is_rgmii(state->interface); | ||||
|  		fallthrough; | ||||
| @@ -130,7 +120,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  			goto unsupported; | ||||
|  		break; | ||||
|  	default: | ||||
| @@ -2018,6 +2039,13 @@ static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port, | ||||
| @@ -2018,6 +2039,13 @@ static void mt7531_sgmii_validate(struct | ||||
|  		phylink_set(supported, 1000baseX_Full); | ||||
|  		phylink_set(supported, 2500baseX_Full); | ||||
|  		phylink_set(supported, 2500baseT_Full); | ||||
| @@ -144,7 +134,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -2166,6 +2194,8 @@ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, | ||||
| @@ -2166,6 +2194,8 @@ mt7531_mac_config(struct dsa_switch *ds, | ||||
|  	case PHY_INTERFACE_MODE_NA: | ||||
|  	case PHY_INTERFACE_MODE_1000BASEX: | ||||
|  	case PHY_INTERFACE_MODE_2500BASEX: | ||||
| @@ -153,7 +143,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  		if (phylink_autoneg_inband(mode)) | ||||
|  			return -EINVAL; | ||||
|   | ||||
| @@ -2303,8 +2333,8 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, | ||||
| @@ -2303,8 +2333,8 @@ static void mt753x_phylink_mac_link_up(s | ||||
|  	/* MT753x MAC works in 1G full duplex mode for all up-clocked | ||||
|  	 * variants. | ||||
|  	 */ | ||||
| @@ -164,7 +154,25 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  		speed = SPEED_1000; | ||||
|  		duplex = DUPLEX_FULL; | ||||
|  	} | ||||
| @@ -2403,8 +2433,8 @@ mt753x_phylink_validate(struct dsa_switch *ds, int port, | ||||
| @@ -2336,7 +2366,7 @@ static int | ||||
|  mt7531_cpu_port_config(struct dsa_switch *ds, int port) | ||||
|  { | ||||
|  	struct mt7530_priv *priv = ds->priv; | ||||
| -	phy_interface_t interface; | ||||
| +	phy_interface_t interface = PHY_INTERFACE_MODE_NA; | ||||
|  	int speed; | ||||
|   | ||||
|  	switch (port) { | ||||
| @@ -2356,6 +2386,8 @@ mt7531_cpu_port_config(struct dsa_switch | ||||
|  		priv->p6_interface = interface; | ||||
|  		break; | ||||
|  	}; | ||||
| +	if (interface == PHY_INTERFACE_MODE_NA) | ||||
| +		dev_err(priv->dev, "invalid interface\n"); | ||||
|   | ||||
|  	if (interface == PHY_INTERFACE_MODE_2500BASEX) | ||||
|  		speed = SPEED_2500; | ||||
| @@ -2403,8 +2435,8 @@ mt753x_phylink_validate(struct dsa_switc | ||||
|   | ||||
|  	phylink_set_port_modes(mask); | ||||
|   | ||||
| @@ -175,7 +183,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  		phylink_set(mask, 10baseT_Half); | ||||
|  		phylink_set(mask, 10baseT_Full); | ||||
|  		phylink_set(mask, 100baseT_Half); | ||||
| @@ -2608,6 +2638,66 @@ mt753x_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) | ||||
| @@ -2608,6 +2640,74 @@ mt753x_phy_write(struct dsa_switch *ds, | ||||
|  	return priv->info->phy_write(ds, port, regnum, val); | ||||
|  } | ||||
|   | ||||
| @@ -191,10 +199,18 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
| +	struct mt7530_priv *priv = ds->priv; | ||||
| +	u32 unused_pm = 0; | ||||
| +	int ret, i; | ||||
| +	struct regmap *reset; | ||||
| + | ||||
| +	/* Reset the switch through internal reset */ | ||||
| +	mt7530_write(priv, MT7530_SYS_CTRL, | ||||
| +		     SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST); | ||||
| +	reset = syscon_regmap_lookup_by_phandle(priv->dev->of_node, "mediatek,sysctrl"); | ||||
| +	if (IS_ERR(reset)) { | ||||
| +		dev_err(priv->dev, "Reset failed\n"); | ||||
| +		return -ENODEV; | ||||
| +	} | ||||
| +	regmap_write(reset, 8, 0x200); | ||||
| +	udelay(20); | ||||
| +	regmap_write(reset, 8, 0); | ||||
| +	udelay(20); | ||||
| + | ||||
| +	/* BPDU to CPU port */ | ||||
| +	mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK, | ||||
| @@ -242,7 +258,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  static const struct dsa_switch_ops mt7530_switch_ops = { | ||||
|  	.get_tag_protocol	= mtk_get_tag_protocol, | ||||
|  	.setup			= mt753x_setup, | ||||
| @@ -2677,12 +2767,28 @@ static const struct mt753x_info mt753x_table[] = { | ||||
| @@ -2677,12 +2777,28 @@ static const struct mt753x_info mt753x_t | ||||
|  		.mac_pcs_an_restart = mt7531_sgmii_restart_an, | ||||
|  		.mac_pcs_link_up = mt7531_sgmii_link_up_force, | ||||
|  	}, | ||||
| @@ -271,7 +287,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  	{ /* sentinel */ }, | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, mt7530_of_match); | ||||
| @@ -2692,6 +2798,7 @@ mt7530_probe(struct mdio_device *mdiodev) | ||||
| @@ -2692,6 +2808,7 @@ mt7530_probe(struct mdio_device *mdiodev | ||||
|  { | ||||
|  	struct mt7530_priv *priv; | ||||
|  	struct device_node *dn; | ||||
| @@ -279,7 +295,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  	int ret; | ||||
|   | ||||
|  	dn = mdiodev->dev.of_node; | ||||
| @@ -2761,6 +2868,16 @@ mt7530_probe(struct mdio_device *mdiodev) | ||||
| @@ -2761,6 +2878,16 @@ mt7530_probe(struct mdio_device *mdiodev | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| @@ -296,7 +312,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  	priv->bus = mdiodev->bus; | ||||
|  	priv->dev = &mdiodev->dev; | ||||
|  	priv->ds->priv = priv; | ||||
| @@ -2769,9 +2886,12 @@ mt7530_probe(struct mdio_device *mdiodev) | ||||
| @@ -2769,9 +2896,12 @@ mt7530_probe(struct mdio_device *mdiodev | ||||
|  	dev_set_drvdata(&mdiodev->dev, priv); | ||||
|   | ||||
|  	ret = dsa_register_switch(priv->ds); | ||||
| @@ -311,7 +327,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  	mt7530_nl_init(&priv); | ||||
|   | ||||
|  	return 0; | ||||
| @@ -2796,6 +2916,9 @@ mt7530_remove(struct mdio_device *mdiodev) | ||||
| @@ -2796,6 +2926,9 @@ mt7530_remove(struct mdio_device *mdiode | ||||
|  	dsa_unregister_switch(priv->ds); | ||||
|  	mutex_destroy(&priv->reg_mutex); | ||||
|   | ||||
| @@ -321,8 +337,7 @@ index 63f8a632b..2cd5dae9c 100644 | ||||
|  	mt7530_nl_exit(); | ||||
|  } | ||||
|   | ||||
| diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h | ||||
| index 130d7e5ec..7b175c5f2 100644 | ||||
| =================================================================== | ||||
| --- a/drivers/net/dsa/mt7530.h | ||||
| +++ b/drivers/net/dsa/mt7530.h | ||||
| @@ -16,6 +16,7 @@ enum mt753x_id { | ||||
| @@ -366,6 +381,3 @@ index 130d7e5ec..7b175c5f2 100644 | ||||
|  	const struct mt753x_info *info; | ||||
|  	unsigned int		id; | ||||
|  	bool			mcm; | ||||
| --  | ||||
| 2.34.1 | ||||
|  | ||||
|   | ||||
| @@ -1,18 +1,7 @@ | ||||
| From c816d165754d8fd002478cce6eb774b9390c795f Mon Sep 17 00:00:00 2001 | ||||
| From: Sam Shih <sam.shih@mediatek.com> | ||||
| Date: Fri, 2 Jun 2023 13:06:01 +0800 | ||||
| Subject: [PATCH]  | ||||
|  [backport-networking-drivers][999-1707-add-mdio-bus-for-gphy-calibration.patch] | ||||
|  | ||||
| --- | ||||
|  drivers/net/dsa/mt7530.c | 115 +++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 115 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c | ||||
| index 2cd5dae9c..290a2e77a 100644 | ||||
| =================================================================== | ||||
| --- a/drivers/net/dsa/mt7530.c | ||||
| +++ b/drivers/net/dsa/mt7530.c | ||||
| @@ -847,6 +847,117 @@ mt7531_ind_phy_write(struct dsa_switch *ds, int port, int regnum, | ||||
| @@ -847,6 +847,117 @@ mt7531_ind_phy_write(struct dsa_switch * | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| @@ -130,7 +119,7 @@ index 2cd5dae9c..290a2e77a 100644 | ||||
|  static void | ||||
|  mt7530_get_strings(struct dsa_switch *ds, int port, u32 stringset, | ||||
|  		   uint8_t *data) | ||||
| @@ -2695,6 +2806,10 @@ mt7988_setup(struct dsa_switch *ds) | ||||
| @@ -2705,6 +2816,10 @@ mt7988_setup(struct dsa_switch *ds) | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
|   | ||||
| @@ -141,6 +130,3 @@ index 2cd5dae9c..290a2e77a 100644 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| --  | ||||
| 2.34.1 | ||||
|  | ||||
|   | ||||
| @@ -4,32 +4,9 @@ Date: Fri, 2 Jun 2023 13:06:32 +0800 | ||||
| Subject: [PATCH] [networking][999-2720-net-dsa-phy-coverity-scan.patch] | ||||
|  | ||||
| --- | ||||
|  drivers/net/dsa/mt7530.c     |  4 ++- | ||||
|  drivers/net/dsa/mt7531_phy.c | 56 +++++++++++++++++++----------------- | ||||
|  2 files changed, 32 insertions(+), 28 deletions(-) | ||||
|  1 files changed, 32 insertions(+), 28 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c | ||||
| index 290a2e77a..21fa3e300 100644 | ||||
| --- a/drivers/net/dsa/mt7530.c | ||||
| +++ b/drivers/net/dsa/mt7530.c | ||||
| @@ -2477,7 +2477,7 @@ static int | ||||
|  mt7531_cpu_port_config(struct dsa_switch *ds, int port) | ||||
|  { | ||||
|  	struct mt7530_priv *priv = ds->priv; | ||||
| -	phy_interface_t interface; | ||||
| +	phy_interface_t interface = PHY_INTERFACE_MODE_NA; | ||||
|  	int speed; | ||||
|   | ||||
|  	switch (port) { | ||||
| @@ -2497,6 +2497,8 @@ mt7531_cpu_port_config(struct dsa_switch *ds, int port) | ||||
|  		priv->p6_interface = interface; | ||||
|  		break; | ||||
|  	}; | ||||
| +	if (interface == PHY_INTERFACE_MODE_NA) | ||||
| +		dev_err(priv->dev, "invalid interface\n"); | ||||
|   | ||||
|  	if (interface == PHY_INTERFACE_MODE_2500BASEX) | ||||
|  		speed = SPEED_2500; | ||||
| diff --git a/drivers/net/dsa/mt7531_phy.c b/drivers/net/dsa/mt7531_phy.c | ||||
| index a5c1e7d54..aaa03c678 100644 | ||||
| --- a/drivers/net/dsa/mt7531_phy.c | ||||
|   | ||||
| @@ -1,3 +1,15 @@ | ||||
| From 535fdc6dfce7def996a5188819ffc96231c36f98 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Tue, 2 Jan 2024 18:13:43 +0800 | ||||
| Subject: [PATCH] [networking][999-2738-an8801sb-gphy-support.patch] | ||||
|  | ||||
| --- | ||||
|  drivers/net/phy/Kconfig  |   5 + | ||||
|  drivers/net/phy/Makefile |   1 + | ||||
|  2 files changed, 6 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig | ||||
| index ccd3f3f..5dbfb17 100644 | ||||
| --- a/drivers/net/phy/Kconfig | ||||
| +++ b/drivers/net/phy/Kconfig | ||||
| @@ -345,6 +345,11 @@ config SFP | ||||
| @@ -12,6 +24,8 @@ | ||||
|  config AIROHA_EN8801SC_PHY | ||||
|  	tristate "Drivers for Airoha EN8801S Gigabit PHYs for MediaTek SoC." | ||||
|  	---help--- | ||||
| diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile | ||||
| index 1e8d67b..d39e54b 100644 | ||||
| --- a/drivers/net/phy/Makefile | ||||
| +++ b/drivers/net/phy/Makefile | ||||
| @@ -74,6 +74,7 @@ endif | ||||
| @@ -22,64 +36,6 @@ | ||||
|  obj-$(CONFIG_AIROHA_EN8801SC_PHY)	+= en8801sc.o | ||||
|  air_en8811h-y := air_en8811h_main.o air_en8811h_api.o | ||||
|  obj-$(CONFIG_AIROHA_EN8811H_PHY)	+= air_en8811h.o | ||||
| --- a/drivers/net/phy/phylink.c | ||||
| +++ b/drivers/net/phy/phylink.c | ||||
| @@ -870,12 +870,17 @@ | ||||
|  	of_node_put(phy_node); | ||||
|   | ||||
|  	if (!phy_dev) | ||||
| -		return -ENODEV; | ||||
| - | ||||
| +	{ | ||||
| +		phylink_info(pl, "[phylink] reload phy-handle2. %s %d\n",__func__, __LINE__); | ||||
| +		phy_node = of_parse_phandle(dn, "phy-handle2", 0); | ||||
| +		phy_dev = of_phy_attach(pl->netdev, phy_node, flags, pl->link_interface); | ||||
| +		if (!phy_dev) | ||||
| +			return -ENODEV; | ||||
| +	} | ||||
| +	 | ||||
|  	ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); | ||||
|  	if (ret) | ||||
|  		phy_detach(phy_dev); | ||||
| - | ||||
|  	return ret; | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(phylink_of_phy_connect); | ||||
| --- a/drivers/of/of_mdio.c | ||||
| +++ b/drivers/of/of_mdio.c | ||||
| @@ -226,7 +226,9 @@ | ||||
|  		return rc; | ||||
|   | ||||
|  	/* Loop over the child nodes and register a phy_device for each phy */ | ||||
| +	int an8801=0; | ||||
|  	for_each_available_child_of_node(np, child) { | ||||
| +		if(an8801==1)break; | ||||
|  		addr = of_mdio_parse_addr(&mdio->dev, child); | ||||
|  		if (addr < 0) { | ||||
|  			scanphys = true; | ||||
| @@ -234,7 +236,25 @@ | ||||
|  		} | ||||
|   | ||||
|  		if (of_mdiobus_child_is_phy(child)) | ||||
| +		{ | ||||
| +			if(addr==30) | ||||
| +			{ | ||||
| +				int phy_id ; | ||||
| + | ||||
| +				phy_id = mdiobus_read(mdio, addr, MII_PHYSID1) << 16 ; | ||||
| +				phy_id = phy_id + mdiobus_read(mdio, addr, MII_PHYSID2); | ||||
| +				dev_info(&mdio->dev, "[of_mdio] %s %d  addr:%d phy_id:0x%x  \n",__func__, __LINE__, addr, phy_id); | ||||
| + | ||||
| +				if (phy_id==0 || phy_id==0x1a750000) | ||||
| +				{ | ||||
| +					dev_info(&mdio->dev, "[of_mdio] %s %d  continue  \n",__func__, __LINE__); | ||||
| +					continue; | ||||
| +				} | ||||
| +				else | ||||
| +					an8801=1; | ||||
| +			} | ||||
|  			rc = of_mdiobus_register_phy(mdio, child, addr); | ||||
| +		} | ||||
|  		else | ||||
|  			rc = of_mdiobus_register_device(mdio, child, addr); | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,397 @@ | ||||
| --- a/drivers/crypto/inside-secure/safexcel.c | ||||
| +++ b/drivers/crypto/inside-secure/safexcel.c | ||||
| @@ -1222,6 +1222,7 @@ static struct safexcel_alg_template *saf | ||||
|  	&safexcel_alg_cfb_aes, | ||||
|  	&safexcel_alg_ofb_aes, | ||||
|  	&safexcel_alg_ctr_aes, | ||||
| +	&safexcel_alg_basic_ctr_aes, | ||||
|  	&safexcel_alg_md5, | ||||
|  	&safexcel_alg_sha1, | ||||
|  	&safexcel_alg_sha224, | ||||
| --- a/drivers/crypto/inside-secure/safexcel.h | ||||
| +++ b/drivers/crypto/inside-secure/safexcel.h | ||||
| @@ -930,6 +930,7 @@ extern struct safexcel_alg_template safe | ||||
|  extern struct safexcel_alg_template safexcel_alg_cfb_aes; | ||||
|  extern struct safexcel_alg_template safexcel_alg_ofb_aes; | ||||
|  extern struct safexcel_alg_template safexcel_alg_ctr_aes; | ||||
| +extern struct safexcel_alg_template safexcel_alg_basic_ctr_aes; | ||||
|  extern struct safexcel_alg_template safexcel_alg_md5; | ||||
|  extern struct safexcel_alg_template safexcel_alg_sha1; | ||||
|  extern struct safexcel_alg_template safexcel_alg_sha224; | ||||
| --- a/drivers/crypto/inside-secure/safexcel_cipher.c | ||||
| +++ b/drivers/crypto/inside-secure/safexcel_cipher.c | ||||
| @@ -51,6 +51,8 @@ struct safexcel_cipher_ctx { | ||||
|  	u8 xcm;  /* 0=authenc, 1=GCM, 2 reserved for CCM */ | ||||
|  	u8 aadskip; | ||||
|  	u8 blocksz; | ||||
| +	bool basic_ctr; | ||||
| +	u32 processed; | ||||
|  	u32 ivmask; | ||||
|  	u32 ctrinit; | ||||
|   | ||||
| @@ -79,7 +81,7 @@ struct safexcel_cipher_req { | ||||
|  static int safexcel_skcipher_iv(struct safexcel_cipher_ctx *ctx, u8 *iv, | ||||
|  				struct safexcel_command_desc *cdesc) | ||||
|  { | ||||
| -	if (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CTR_LOAD) { | ||||
| +	if (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CTR_LOAD && !(ctx->basic_ctr)) { | ||||
|  		cdesc->control_data.options |= EIP197_OPTION_4_TOKEN_IV_CMD; | ||||
|  		/* 32 bit nonce */ | ||||
|  		cdesc->control_data.token[0] = ctx->nonce; | ||||
| @@ -513,8 +515,8 @@ static int safexcel_aead_setkey(struct c | ||||
|  	memcpy(ctx->opad, &ostate.state, ctx->state_sz); | ||||
|   | ||||
|  	memzero_explicit(&keys, sizeof(keys)); | ||||
| -	return 0; | ||||
|   | ||||
| +	return 0; | ||||
|  badkey: | ||||
|  	memzero_explicit(&keys, sizeof(keys)); | ||||
|  	return err; | ||||
| @@ -622,6 +624,43 @@ static int safexcel_context_control(stru | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int safexcel_queue_req(struct crypto_async_request *base, | ||||
| +			struct safexcel_cipher_req *sreq, | ||||
| +			enum safexcel_cipher_direction dir) | ||||
| +{ | ||||
| +	struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(base->tfm); | ||||
| +	struct safexcel_crypto_priv *priv = ctx->priv; | ||||
| +	int ret, ring; | ||||
| + | ||||
| +	sreq->needs_inv = false; | ||||
| +	sreq->direction = dir; | ||||
| + | ||||
| +	if (ctx->base.ctxr) { | ||||
| +		if (priv->flags & EIP197_TRC_CACHE && ctx->base.needs_inv) { | ||||
| +			sreq->needs_inv = true; | ||||
| +			ctx->base.needs_inv = false; | ||||
| +		} | ||||
| +	} else { | ||||
| +		ctx->base.ring = safexcel_select_ring(priv); | ||||
| +		ctx->base.ctxr = dma_pool_zalloc(priv->context_pool, | ||||
| +						 EIP197_GFP_FLAGS(*base), | ||||
| +						 &ctx->base.ctxr_dma); | ||||
| +		if (!ctx->base.ctxr) | ||||
| +			return -ENOMEM; | ||||
| +	} | ||||
| + | ||||
| +	ring = ctx->base.ring; | ||||
| + | ||||
| +	spin_lock_bh(&priv->ring[ring].queue_lock); | ||||
| +	ret = crypto_enqueue_request(&priv->ring[ring].queue, base); | ||||
| +	spin_unlock_bh(&priv->ring[ring].queue_lock); | ||||
| + | ||||
| +	queue_work(priv->ring[ring].workqueue, | ||||
| +		   &priv->ring[ring].work_data.work); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  static int safexcel_handle_req_result(struct safexcel_crypto_priv *priv, int ring, | ||||
|  				      struct crypto_async_request *async, | ||||
|  				      struct scatterlist *src, | ||||
| @@ -635,6 +674,7 @@ static int safexcel_handle_req_result(st | ||||
|  	struct safexcel_cipher_ctx *ctx = crypto_skcipher_ctx(skcipher); | ||||
|  	struct safexcel_result_desc *rdesc; | ||||
|  	int ndesc = 0; | ||||
| +	int flag; | ||||
|   | ||||
|  	*ret = 0; | ||||
|   | ||||
| @@ -677,7 +717,13 @@ static int safexcel_handle_req_result(st | ||||
|  				    crypto_skcipher_ivsize(skcipher))); | ||||
|  	} | ||||
|   | ||||
| -	*should_complete = true; | ||||
| +	if (ctx->basic_ctr && ctx->processed != cryptlen) { | ||||
| +		*should_complete = false; | ||||
| +		flag = safexcel_queue_req(async, sreq, sreq->direction); | ||||
| +	} else { | ||||
| +		*should_complete = true; | ||||
| +		ctx->processed = 0; | ||||
| +	} | ||||
|   | ||||
|  	return ndesc; | ||||
|  } | ||||
| @@ -700,12 +746,16 @@ static int safexcel_send_req(struct cryp | ||||
|  	unsigned int totlen; | ||||
|  	unsigned int totlen_src = cryptlen + assoclen; | ||||
|  	unsigned int totlen_dst = totlen_src; | ||||
| +	unsigned int pass_byte = 0; | ||||
| +	unsigned int pass; | ||||
|  	struct safexcel_token *atoken; | ||||
|  	int n_cdesc = 0, n_rdesc = 0; | ||||
|  	int queued, i, ret = 0; | ||||
|  	bool first = true; | ||||
|   | ||||
| -	sreq->nr_src = sg_nents_for_len(src, totlen_src); | ||||
| +	pass_byte = ctx->processed; | ||||
| +	pass = pass_byte; | ||||
| +	sreq->nr_src = sg_nents_for_len(src, totlen_src + pass_byte); | ||||
|   | ||||
|  	if (ctx->aead) { | ||||
|  		/* | ||||
| @@ -736,7 +786,7 @@ static int safexcel_send_req(struct cryp | ||||
|  				    crypto_skcipher_ivsize(skcipher))); | ||||
|  	} | ||||
|   | ||||
| -	sreq->nr_dst = sg_nents_for_len(dst, totlen_dst); | ||||
| +	sreq->nr_dst = sg_nents_for_len(dst, totlen_dst + pass_byte); | ||||
|   | ||||
|  	/* | ||||
|  	 * Remember actual input length, source buffer length may be | ||||
| @@ -798,14 +848,23 @@ static int safexcel_send_req(struct cryp | ||||
|  	for_each_sg(src, sg, sreq->nr_src, i) { | ||||
|  		int len = sg_dma_len(sg); | ||||
|   | ||||
| +		if (pass) { | ||||
| +			if (pass >= len) { | ||||
| +				pass -= len; | ||||
| +				continue; | ||||
| +			} | ||||
| +			len = len - pass; | ||||
| +		} | ||||
|  		/* Do not overflow the request */ | ||||
|  		if (queued < len) | ||||
|  			len = queued; | ||||
|   | ||||
|  		cdesc = safexcel_add_cdesc(priv, ring, !n_cdesc, | ||||
| -					   !(queued - len), | ||||
| -					   sg_dma_address(sg), len, totlen, | ||||
| -					   ctx->base.ctxr_dma, &atoken); | ||||
| +					!(queued - len), | ||||
| +					sg_dma_address(sg) + pass, len, | ||||
| +					totlen, ctx->base.ctxr_dma, &atoken); | ||||
| +		pass = 0; | ||||
| + | ||||
|  		if (IS_ERR(cdesc)) { | ||||
|  			/* No space left in the command descriptor ring */ | ||||
|  			ret = PTR_ERR(cdesc); | ||||
| @@ -820,6 +879,7 @@ static int safexcel_send_req(struct cryp | ||||
|  		if (!queued) | ||||
|  			break; | ||||
|  	} | ||||
| + | ||||
|  skip_cdesc: | ||||
|  	/* Add context control words and token to first command descriptor */ | ||||
|  	safexcel_context_control(ctx, base, sreq, first_cdesc); | ||||
| @@ -831,11 +891,20 @@ skip_cdesc: | ||||
|  		safexcel_skcipher_token(ctx, iv, first_cdesc, atoken, | ||||
|  					cryptlen); | ||||
|   | ||||
| +	pass = pass_byte; | ||||
|  	/* result descriptors */ | ||||
|  	for_each_sg(dst, sg, sreq->nr_dst, i) { | ||||
|  		bool last = (i == sreq->nr_dst - 1); | ||||
|  		u32 len = sg_dma_len(sg); | ||||
|   | ||||
| +		if (pass) { | ||||
| +			if (pass >= len) { | ||||
| +				pass -= len; | ||||
| +				continue; | ||||
| +			} | ||||
| +			len -= pass; | ||||
| +		} | ||||
| + | ||||
|  		/* only allow the part of the buffer we know we need */ | ||||
|  		if (len > totlen_dst) | ||||
|  			len = totlen_dst; | ||||
| @@ -855,9 +924,11 @@ skip_cdesc: | ||||
|  						   len - assoclen); | ||||
|  			assoclen = 0; | ||||
|  		} else { | ||||
| + | ||||
|  			rdesc = safexcel_add_rdesc(priv, ring, first, last, | ||||
| -						   sg_dma_address(sg), | ||||
| -						   len); | ||||
| +					   sg_dma_address(sg) + pass, | ||||
| +					   len); | ||||
| +			pass = 0; | ||||
|  		} | ||||
|  		if (IS_ERR(rdesc)) { | ||||
|  			/* No space left in the result descriptor ring */ | ||||
| @@ -892,6 +963,7 @@ skip_cdesc: | ||||
|   | ||||
|  	*commands = n_cdesc; | ||||
|  	*results = n_rdesc; | ||||
| + | ||||
|  	return 0; | ||||
|   | ||||
|  rdesc_rollback: | ||||
| @@ -1033,6 +1105,26 @@ static int safexcel_cipher_send_inv(stru | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static void accum_iv(u8 *iv, u32 blocks) | ||||
| +{ | ||||
| +	u32 *counter; | ||||
| +	int i; | ||||
| + | ||||
| +	for (i = 12; i >= 0; i = i - 4) { | ||||
| +		counter = (u32 *) &iv[i]; | ||||
| +		if (be32_to_cpu(*counter) + blocks >= be32_to_cpu(*counter)) { | ||||
| +			*counter = cpu_to_be32(be32_to_cpu(*counter) + blocks); | ||||
| +			blocks = 0; | ||||
| +		} else { | ||||
| +			*counter = cpu_to_be32(be32_to_cpu(*counter) + blocks); | ||||
| +			blocks = 1; | ||||
| +		} | ||||
| + | ||||
| +		if (blocks == 0) | ||||
| +			break; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  static int safexcel_skcipher_send(struct crypto_async_request *async, int ring, | ||||
|  				  int *commands, int *results) | ||||
|  { | ||||
| @@ -1049,6 +1141,8 @@ static int safexcel_skcipher_send(struct | ||||
|  	} else { | ||||
|  		struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); | ||||
|  		u8 input_iv[AES_BLOCK_SIZE]; | ||||
| +		u32 blocks; | ||||
| +		u32 *counter; | ||||
|   | ||||
|  		/* | ||||
|  		 * Save input IV in case of CBC decrypt mode | ||||
| @@ -1056,9 +1150,29 @@ static int safexcel_skcipher_send(struct | ||||
|  		 */ | ||||
|  		memcpy(input_iv, req->iv, crypto_skcipher_ivsize(skcipher)); | ||||
|   | ||||
| -		ret = safexcel_send_req(async, ring, sreq, req->src, | ||||
| +		if (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CTR_LOAD && ctx->basic_ctr) { | ||||
| +			counter = (u32 *) &req->iv[12]; | ||||
| +			blocks = (req->cryptlen / ctx->blocksz) - (ctx->processed / 16); | ||||
| +			if (req->cryptlen % ctx->blocksz) | ||||
| +				blocks++; | ||||
| +			if (be32_to_cpu(*counter) + blocks < be32_to_cpu(*counter)) { | ||||
| +				blocks = 0 - be32_to_cpu(*counter); | ||||
| +				ret = safexcel_send_req(async, ring, sreq, req->src, | ||||
| +						req->dst, min(blocks * AES_BLOCK_SIZE, req->cryptlen), 0, 0, input_iv, | ||||
| +						commands, results); | ||||
| +				ctx->processed += min(blocks * AES_BLOCK_SIZE, req->cryptlen); | ||||
| +			} else { | ||||
| +				ret = safexcel_send_req(async, ring, sreq, req->src, | ||||
| +						req->dst, req->cryptlen - ctx->processed, | ||||
| +						0, 0, input_iv, commands, results); | ||||
| +				ctx->processed = req->cryptlen; | ||||
| +			} | ||||
| +			accum_iv(req->iv, blocks); | ||||
| +		} else { | ||||
| +			ret = safexcel_send_req(async, ring, sreq, req->src, | ||||
|  					req->dst, req->cryptlen, 0, 0, input_iv, | ||||
|  					commands, results); | ||||
| +		} | ||||
|  	} | ||||
|   | ||||
|  	sreq->rdescs = *results; | ||||
| @@ -1152,43 +1266,6 @@ static int safexcel_aead_exit_inv(struct | ||||
|  	return safexcel_cipher_exit_inv(tfm, &req->base, sreq, &result); | ||||
|  } | ||||
|   | ||||
| -static int safexcel_queue_req(struct crypto_async_request *base, | ||||
| -			struct safexcel_cipher_req *sreq, | ||||
| -			enum safexcel_cipher_direction dir) | ||||
| -{ | ||||
| -	struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(base->tfm); | ||||
| -	struct safexcel_crypto_priv *priv = ctx->priv; | ||||
| -	int ret, ring; | ||||
| - | ||||
| -	sreq->needs_inv = false; | ||||
| -	sreq->direction = dir; | ||||
| - | ||||
| -	if (ctx->base.ctxr) { | ||||
| -		if (priv->flags & EIP197_TRC_CACHE && ctx->base.needs_inv) { | ||||
| -			sreq->needs_inv = true; | ||||
| -			ctx->base.needs_inv = false; | ||||
| -		} | ||||
| -	} else { | ||||
| -		ctx->base.ring = safexcel_select_ring(priv); | ||||
| -		ctx->base.ctxr = dma_pool_zalloc(priv->context_pool, | ||||
| -						 EIP197_GFP_FLAGS(*base), | ||||
| -						 &ctx->base.ctxr_dma); | ||||
| -		if (!ctx->base.ctxr) | ||||
| -			return -ENOMEM; | ||||
| -	} | ||||
| - | ||||
| -	ring = ctx->base.ring; | ||||
| - | ||||
| -	spin_lock_bh(&priv->ring[ring].queue_lock); | ||||
| -	ret = crypto_enqueue_request(&priv->ring[ring].queue, base); | ||||
| -	spin_unlock_bh(&priv->ring[ring].queue_lock); | ||||
| - | ||||
| -	queue_work(priv->ring[ring].workqueue, | ||||
| -		   &priv->ring[ring].work_data.work); | ||||
| - | ||||
| -	return ret; | ||||
| -} | ||||
| - | ||||
|  static int safexcel_encrypt(struct skcipher_request *req) | ||||
|  { | ||||
|  	return safexcel_queue_req(&req->base, skcipher_request_ctx(req), | ||||
| @@ -1216,6 +1293,8 @@ static int safexcel_skcipher_cra_init(st | ||||
|  	ctx->base.send = safexcel_skcipher_send; | ||||
|  	ctx->base.handle_result = safexcel_skcipher_handle_result; | ||||
|  	ctx->ivmask = EIP197_OPTION_4_TOKEN_IV_CMD; | ||||
| +	ctx->basic_ctr = false; | ||||
| +	ctx->processed = 0; | ||||
|  	ctx->ctrinit = 1; | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1496,6 +1575,44 @@ struct safexcel_alg_template safexcel_al | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| +static int safexcel_skcipher_basic_aes_ctr_cra_init(struct crypto_tfm *tfm) | ||||
| +{ | ||||
| +	struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); | ||||
| + | ||||
| +	safexcel_skcipher_cra_init(tfm); | ||||
| +	ctx->alg = SAFEXCEL_AES; | ||||
| +	ctx->blocksz = AES_BLOCK_SIZE; | ||||
| +	ctx->mode = CONTEXT_CONTROL_CRYPTO_MODE_CTR_LOAD; | ||||
| +	ctx->basic_ctr = true; | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +struct safexcel_alg_template safexcel_alg_basic_ctr_aes = { | ||||
| +	.type = SAFEXCEL_ALG_TYPE_SKCIPHER, | ||||
| +	.algo_mask = SAFEXCEL_ALG_AES, | ||||
| +	.alg.skcipher = { | ||||
| +		.setkey = safexcel_skcipher_aes_setkey, | ||||
| +		.encrypt = safexcel_encrypt, | ||||
| +		.decrypt = safexcel_decrypt, | ||||
| +		.min_keysize = AES_MIN_KEY_SIZE, | ||||
| +		.max_keysize = AES_MAX_KEY_SIZE, | ||||
| +		.ivsize = AES_BLOCK_SIZE, | ||||
| +		.base = { | ||||
| +			.cra_name = "ctr(aes)", | ||||
| +			.cra_driver_name = "safexcel-basic-ctr-aes", | ||||
| +			.cra_priority = SAFEXCEL_CRA_PRIORITY, | ||||
| +			.cra_flags = CRYPTO_ALG_ASYNC | | ||||
| +					 CRYPTO_ALG_KERN_DRIVER_ONLY, | ||||
| +			.cra_blocksize = 1, | ||||
| +			.cra_ctxsize = sizeof(struct safexcel_cipher_ctx), | ||||
| +			.cra_alignmask = 0, | ||||
| +			.cra_init = safexcel_skcipher_basic_aes_ctr_cra_init, | ||||
| +			.cra_exit = safexcel_skcipher_cra_exit, | ||||
| +			.cra_module = THIS_MODULE, | ||||
| +		}, | ||||
| +	}, | ||||
| +}; | ||||
| + | ||||
|  static int safexcel_des_setkey(struct crypto_skcipher *ctfm, const u8 *key, | ||||
|  			       unsigned int len) | ||||
|  { | ||||
| @@ -1724,6 +1841,9 @@ static int safexcel_aead_cra_init(struct | ||||
|  	ctx->aead = true; | ||||
|  	ctx->base.send = safexcel_aead_send; | ||||
|  	ctx->base.handle_result = safexcel_aead_handle_result; | ||||
| +	ctx->basic_ctr = false; | ||||
| +	ctx->processed = 0; | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -0,0 +1,178 @@ | ||||
| diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig | ||||
| index 3c2ea9f..e5a8b70 100644 | ||||
| --- a/drivers/net/phy/Kconfig | ||||
| +++ b/drivers/net/phy/Kconfig | ||||
| @@ -412,11 +412,25 @@ choice | ||||
|  endchoice | ||||
|   | ||||
|  config AQUANTIA_PHY_FW_FILE | ||||
| -	string "FW File" | ||||
| +	string "Default PHY FW File" | ||||
|  	depends on AQUANTIA_PHY | ||||
|  	default "Rhe-05.06-Candidate7-AQR_Mediatek_23B_StartOff_ID45623_VER36657.cld" | ||||
|  	---help--- | ||||
| -	  Currently supports the Aquantia AQR113c | ||||
| +	  This is the default FW. | ||||
| + | ||||
| +config AQUANTIA_PHY_FW_FILE_AQR113C | ||||
| +	string "AQR113C PHY FW File" | ||||
| +	depends on AQUANTIA_PHY | ||||
| +	default "Rhe-05.06-Candidate7-AQR_Mediatek_23B_StartOff_ID45623_VER36657.cld" | ||||
| +	---help--- | ||||
| +	  This FW is for AQR113C | ||||
| + | ||||
| +config AQUANTIA_PHY_FW_FILE_CUX3410 | ||||
| +	string "CUX3410 PHY FW File" | ||||
| +	depends on AQUANTIA_PHY | ||||
| +	default "AQR-G4_v5.7.0-AQR_EVB_Generic_X3410_StdCfg_MDISwap_USX_ID46316_VER2148.cld" | ||||
| +	---help--- | ||||
| +	  This FW is for CUX3410 | ||||
|   | ||||
|  config AQUANTIA_PHY_MIB | ||||
|  	tristate "MIB Read Enable" | ||||
| diff --git a/drivers/net/phy/aquantia.h b/drivers/net/phy/aquantia.h | ||||
| index 03d6744..d7e6786 100644 | ||||
| --- a/drivers/net/phy/aquantia.h | ||||
| +++ b/drivers/net/phy/aquantia.h | ||||
| @@ -9,6 +9,16 @@ | ||||
|  #include <linux/device.h> | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
| +#define PHY_ID_AQ1202	0x03a1b445 | ||||
| +#define PHY_ID_AQ2104	0x03a1b460 | ||||
| +#define PHY_ID_AQR105	0x03a1b4a2 | ||||
| +#define PHY_ID_AQR106	0x03a1b4d0 | ||||
| +#define PHY_ID_AQR107	0x03a1b4e0 | ||||
| +#define PHY_ID_AQCS109	0x03a1b5c2 | ||||
| +#define PHY_ID_AQR405	0x03a1b4b0 | ||||
| +#define PHY_ID_AQR113C	0x31c31c12 | ||||
| +#define PHY_ID_CUX3410	0x31c31dd3 | ||||
| + | ||||
|  #define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT		4 | ||||
|   | ||||
|  #define PMAPMD_RSVD_VEND_PROV				0xe400 | ||||
| diff --git a/drivers/net/phy/aquantia_firmware.c b/drivers/net/phy/aquantia_firmware.c | ||||
| index f37bee1..55a9a29 100644 | ||||
| --- a/drivers/net/phy/aquantia_firmware.c | ||||
| +++ b/drivers/net/phy/aquantia_firmware.c | ||||
| @@ -19,6 +19,8 @@ | ||||
|  #endif | ||||
|   | ||||
|  #define AQR_FIRMWARE					CONFIG_AQUANTIA_PHY_FW_FILE | ||||
| +#define AQR113C_FIRMWARE				CONFIG_AQUANTIA_PHY_FW_FILE_AQR113C | ||||
| +#define CUX3410_FIRMWARE				CONFIG_AQUANTIA_PHY_FW_FILE_CUX3410 | ||||
|   | ||||
|  /* Vendor specific 1, MDIO_MMD_VEND1 */ | ||||
|  #define VEND1_STD_CONTROL1				0x0000 | ||||
| @@ -923,6 +925,18 @@ int aqr_firmware_heartbeat_thread(void *data) | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static char* aqr_firmware_name_get(u32 phy_id) | ||||
| +{ | ||||
| +	switch (phy_id) { | ||||
| +		case PHY_ID_AQR113C: | ||||
| +			return AQR113C_FIRMWARE; | ||||
| +		case PHY_ID_CUX3410: | ||||
| +			return CUX3410_FIRMWARE; | ||||
| +		default: | ||||
| +			return AQR_FIRMWARE; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  static void aqr_firmware_download_cb(const struct firmware *fw, void *context) | ||||
|  { | ||||
|  	struct phy_device **phydevs = context; | ||||
| @@ -931,6 +945,8 @@ static void aqr_firmware_download_cb(const struct firmware *fw, void *context) | ||||
|  	struct aqr107_priv *priv = phydevs[0]->priv; | ||||
|  	int result[MAX_GANGLOAD_DEVICES]; | ||||
|  	int i, num_phydevs = 0, ret = 0; | ||||
| +	u32 phy_id = phydevs[0]->drv->phy_id; | ||||
| +	char *firmware_name = aqr_firmware_name_get(phy_id); | ||||
|   | ||||
|  	if (!fw) | ||||
|  		return; | ||||
| @@ -957,7 +973,7 @@ retry: | ||||
|   | ||||
|  			dev = &phydevs[i]->mdio.dev; | ||||
|  			dev_err(dev, "failed to download firmware %s, ret: %d\n", | ||||
| -				AQR_FIRMWARE, ret); | ||||
| +				firmware_name, ret); | ||||
|  			goto retry; | ||||
|  		} | ||||
|  	} | ||||
| @@ -1005,6 +1021,8 @@ static int aqr_firmware_download_single(struct phy_device *phydev, bool force_re | ||||
|  	struct device *dev = &phydev->mdio.dev; | ||||
|  	const struct firmware *fw; | ||||
|  	int ret = 0; | ||||
| +	u32 phy_id = phydev->drv->phy_id; | ||||
| +	char *firmware_name = aqr_firmware_name_get(phy_id); | ||||
|   | ||||
|  	if (priv->fw_initialized == true && force_reload == false) | ||||
|  		return 0; | ||||
| @@ -1016,10 +1034,10 @@ static int aqr_firmware_download_single(struct phy_device *phydev, bool force_re | ||||
|  	priv->fw_dl_mode = FW_DL_SINGLE; | ||||
|  	priv->heartbeat = -1; | ||||
|   | ||||
| -	ret = request_firmware(&fw, AQR_FIRMWARE, dev); | ||||
| +	ret = request_firmware(&fw, firmware_name, dev); | ||||
|  	if (ret) { | ||||
|  		dev_err(dev, "failed to request firmware %s, ret: %d\n", | ||||
| -			AQR_FIRMWARE, ret); | ||||
| +			firmware_name, ret); | ||||
|  	} | ||||
|   | ||||
|  	aqr_firmware_download_cb(fw, priv->phydevs); | ||||
| @@ -1032,6 +1050,8 @@ static int aqr_firmware_gandload_thread(void *data) | ||||
|  	struct phy_device **phydevs = data; | ||||
|  	struct device *dev = &phydevs[0]->mdio.dev; | ||||
|  	int ret = 0; | ||||
| +	u32 phy_id = phydevs[0]->drv->phy_id; | ||||
| +	char *firmware_name = aqr_firmware_name_get(phy_id); | ||||
|   | ||||
|  	for (;;) { | ||||
|  		if (kthread_should_stop()) | ||||
| @@ -1040,11 +1060,11 @@ static int aqr_firmware_gandload_thread(void *data) | ||||
|  		/* either maximum gangload phy devices or timeout is reached */ | ||||
|  		if (gangload == MAX_GANGLOAD_DEVICES || | ||||
|  		    time_after(jiffies, gangload_timeout)) { | ||||
| -			ret = request_firmware_nowait(THIS_MODULE, true, AQR_FIRMWARE, dev, | ||||
| +			ret = request_firmware_nowait(THIS_MODULE, true, firmware_name, dev, | ||||
|  						      GFP_KERNEL, phydevs, aqr_firmware_download_cb); | ||||
|  			if (ret) { | ||||
|  				dev_err(dev, "failed to request firmware %s, ret: %d\n", | ||||
| -					AQR_FIRMWARE, ret); | ||||
| +					firmware_name, ret); | ||||
|  			} | ||||
|  			break; | ||||
|  		} | ||||
| diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c | ||||
| index f445ef9..208fc7d 100644 | ||||
| --- a/drivers/net/phy/aquantia_main.c | ||||
| +++ b/drivers/net/phy/aquantia_main.c | ||||
| @@ -17,16 +17,6 @@ | ||||
|   | ||||
|  #include "aquantia.h" | ||||
|   | ||||
| -#define PHY_ID_AQ1202	0x03a1b445 | ||||
| -#define PHY_ID_AQ2104	0x03a1b460 | ||||
| -#define PHY_ID_AQR105	0x03a1b4a2 | ||||
| -#define PHY_ID_AQR106	0x03a1b4d0 | ||||
| -#define PHY_ID_AQR107	0x03a1b4e0 | ||||
| -#define PHY_ID_AQCS109	0x03a1b5c2 | ||||
| -#define PHY_ID_AQR405	0x03a1b4b0 | ||||
| -#define PHY_ID_AQR113C	0x31c31c12 | ||||
| -#define PHY_ID_CUX3410	0x31c31dd3 | ||||
| - | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS		0xe812 | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK	GENMASK(7, 3) | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR	0 | ||||
| @@ -489,7 +479,7 @@ void aqr107_chip_info(struct phy_device *phydev) | ||||
|  	build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); | ||||
|  	prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); | ||||
|   | ||||
| -	phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", | ||||
| +	phydev_info(phydev, "FW %u.%u, Build %u, Provisioning %u\n", | ||||
|  		   fw_major, fw_minor, build_id, prov_id); | ||||
|  } | ||||
|   | ||||
| @@ -0,0 +1,94 @@ | ||||
| From ae07baf1efdbd3705be90691b5ae606057b225b0 Mon Sep 17 00:00:00 2001 | ||||
| From: "neal.yen" <neal.yen@mediatek.com> | ||||
| Date: Fri, 26 Jul 2024 15:05:19 +0800 | ||||
| Subject: [PATCH] 999-2742-drivers-net-dsa-mxl862xx | ||||
|  | ||||
| --- | ||||
|  drivers/net/dsa/Kconfig  |  2 ++ | ||||
|  drivers/net/dsa/Makefile |  1 + | ||||
|  include/net/dsa.h        |  4 ++++ | ||||
|  net/dsa/Kconfig          | 13 +++++++++++++ | ||||
|  net/dsa/Makefile         |  2 ++ | ||||
|  6 files changed, 23 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig | ||||
| index a15dd0d..13ef0f0 100644 | ||||
| --- a/drivers/net/dsa/Kconfig | ||||
| +++ b/drivers/net/dsa/Kconfig | ||||
| @@ -54,6 +54,8 @@ source "drivers/net/dsa/microchip/Kconfig" | ||||
|   | ||||
|  source "drivers/net/dsa/mv88e6xxx/Kconfig" | ||||
|   | ||||
| +source "drivers/net/dsa/mxl862xx/Kconfig" | ||||
| + | ||||
|  source "drivers/net/dsa/sja1105/Kconfig" | ||||
|   | ||||
|  config NET_DSA_QCA8K | ||||
| diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile | ||||
| index c36e793..263c052 100644 | ||||
| --- a/drivers/net/dsa/Makefile | ||||
| +++ b/drivers/net/dsa/Makefile | ||||
| @@ -23,3 +23,4 @@ obj-y				+= b53/ | ||||
|  obj-y				+= microchip/ | ||||
|  obj-y				+= mv88e6xxx/ | ||||
|  obj-y				+= sja1105/ | ||||
| +obj-y				+= mxl862xx/ | ||||
| diff --git a/include/net/dsa.h b/include/net/dsa.h | ||||
| index 65cfa41..0c138e5 100644 | ||||
| --- a/include/net/dsa.h | ||||
| +++ b/include/net/dsa.h | ||||
| @@ -44,6 +44,8 @@ struct phylink_link_state; | ||||
|  #define DSA_TAG_PROTO_KSZ8795_VALUE		14 | ||||
|  #define DSA_TAG_PROTO_RTL4_A_VALUE		17 | ||||
|  #define DSA_TAG_PROTO_ARHT_VALUE		28 | ||||
| +#define DSA_TAG_PROTO_MXL862_VALUE		29 | ||||
| +#define DSA_TAG_PROTO_MXL862_8021Q_VALUE	30 | ||||
|   | ||||
|  enum dsa_tag_protocol { | ||||
|  	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE, | ||||
| @@ -63,6 +65,8 @@ enum dsa_tag_protocol { | ||||
|  	DSA_TAG_PROTO_KSZ8795		= DSA_TAG_PROTO_KSZ8795_VALUE, | ||||
|  	DSA_TAG_PROTO_RTL4_A		= DSA_TAG_PROTO_RTL4_A_VALUE, | ||||
|  	DSA_TAG_PROTO_ARHT		= DSA_TAG_PROTO_ARHT_VALUE, | ||||
| +	DSA_TAG_PROTO_MXL862		= DSA_TAG_PROTO_MXL862_VALUE, | ||||
| +	DSA_TAG_PROTO_MXL862_8021Q	= DSA_TAG_PROTO_MXL862_8021Q_VALUE, | ||||
|  }; | ||||
|   | ||||
|  struct packet_type; | ||||
| diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig | ||||
| index 5da7a23..b0b9df1 100644 | ||||
| --- a/net/dsa/Kconfig | ||||
| +++ b/net/dsa/Kconfig | ||||
| @@ -86,6 +86,19 @@ config NET_DSA_TAG_KSZ | ||||
|  	  Say Y if you want to enable support for tagging frames for the | ||||
|  	  Microchip 8795/9477/9893 families of switches. | ||||
|   | ||||
| +config NET_DSA_TAG_MXL862 | ||||
| +	tristate "Tag driver for MxL862xx switches" | ||||
| +	help | ||||
| +	  Say Y or M if you want to enable support for tagging frames for the | ||||
| +	  Maxlinear MxL862xx switches. | ||||
| + | ||||
| +config NET_DSA_TAG_MXL862_8021Q | ||||
| +	tristate "Tag driver for MxL862xx switches, based on VLAN tags" | ||||
| +	help | ||||
| +	  Say Y or M if you want to enable support for tagging frames for the | ||||
| +	  Maxlinear MxL862xx switches. This tagging variant is based on 4-byte wide VLAN | ||||
| +	  tags | ||||
| + | ||||
|  config NET_DSA_TAG_RTL4_A | ||||
|  	tristate "Tag driver for Realtek 4 byte protocol A tags" | ||||
|  	help | ||||
| diff --git a/net/dsa/Makefile b/net/dsa/Makefile | ||||
| index b58ac0f..692b70f 100644 | ||||
| --- a/net/dsa/Makefile | ||||
| +++ b/net/dsa/Makefile | ||||
| @@ -17,3 +17,5 @@ obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o | ||||
|  obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o | ||||
|  obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o | ||||
|  obj-$(CONFIG_NET_DSA_TAG_AIROHA) += tag_arht.o | ||||
| +obj-$(CONFIG_NET_DSA_TAG_MXL862) += tag_mxl862xx.o | ||||
| +obj-$(CONFIG_NET_DSA_TAG_MXL862_8021Q) += tag_mxl862xx_8021q.o | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -0,0 +1,71 @@ | ||||
| From 5feba07f7e9ccf9c9a3d862e321c84ac4fd089a9 Mon Sep 17 00:00:00 2001 | ||||
| From: "neal.yen" <neal.yen@mediatek.com> | ||||
| Date: Thu, 26 Sep 2024 20:19:57 +0800 | ||||
| Subject: [PATCH] 999-2743-drivers-net-dsa-mxl862xx-kernel-compatible | ||||
|  | ||||
| --- | ||||
|  drivers/net/dsa/mxl862xx/mxl862xx.c | 32 ----------------------------- | ||||
|  1 file changed, 32 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c | ||||
| index 5cab346..cbf2fe4 100755 | ||||
| --- a/drivers/net/dsa/mxl862xx/mxl862xx.c | ||||
| +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c | ||||
| @@ -1494,7 +1494,6 @@ static void mxl862xx_phylink_mac_link_down(struct dsa_switch *ds, int port, | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -#if (KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE) | ||||
|  static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port, | ||||
|  					 unsigned int mode, | ||||
|  					 phy_interface_t interface, | ||||
| @@ -1589,32 +1588,6 @@ static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port, | ||||
|  	return; | ||||
|  } | ||||
|   | ||||
| -#else | ||||
| -static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port, | ||||
| -					 unsigned int mode, | ||||
| -					 phy_interface_t interface, | ||||
| -					 struct phy_device *phydev) | ||||
| -{ | ||||
| -	mxl862xx_port_link_cfg_t port_link_cfg = { 0 }; | ||||
| -	int ret; | ||||
| - | ||||
| -	if (dsa_is_cpu_port(ds, port)) | ||||
| -		return; | ||||
| - | ||||
| -	port_link_cfg.port_id = port + 1; | ||||
| - | ||||
| -	port_link_cfg.link_force = true; | ||||
| -	port_link_cfg.link = MXL862XX_PORT_LINK_UP; | ||||
| - | ||||
| -	ret = mxl862xx_port_link_cfg_set(&mxl_dev, &port_link_cfg); | ||||
| -	if (ret != MXL862XX_STATUS_OK) { | ||||
| -		dev_err(ds->dev, | ||||
| -			"%s: Port link configuration for port %d failed with %d\n", | ||||
| -			__func__, port, ret); | ||||
| -		return; | ||||
| -	} | ||||
| -} | ||||
| -#endif | ||||
|  #endif | ||||
|   | ||||
|  static void mxl862xx_get_ethtool_stats(struct dsa_switch *ds, int port, | ||||
| @@ -4398,13 +4371,8 @@ static int mxl862xx_change_tag_protocol(struct dsa_switch *ds, | ||||
|  } | ||||
|  #endif | ||||
|   | ||||
| -#if (KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE) | ||||
| -static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, | ||||
| -						       int port) | ||||
| -#else | ||||
|  static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, | ||||
|  						       int port, enum dsa_tag_protocol m) | ||||
| -#endif | ||||
|  { | ||||
|  	enum dsa_tag_protocol tag_proto; | ||||
|   | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -0,0 +1,79 @@ | ||||
| From 2b6996b2827db88d87ad28e0c28cbe7382eb375c Mon Sep 17 00:00:00 2001 | ||||
| From: "neal.yen" <neal.yen@mediatek.com> | ||||
| Date: Mon, 12 Aug 2024 16:32:48 +0800 | ||||
| Subject: [PATCH] 999-2745-drivers-net-phy-mxl862xx-mxl-gpy | ||||
|  | ||||
| --- | ||||
|  drivers/net/phy/mxl-gpy.c | 28 ++++++++++++++++++++++++++++ | ||||
|  1 file changed, 28 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c | ||||
| index 7304278..988eb77 100644 | ||||
| --- a/drivers/net/phy/mxl-gpy.c | ||||
| +++ b/drivers/net/phy/mxl-gpy.c | ||||
| @@ -14,6 +14,7 @@ | ||||
|  /* PHY ID */ | ||||
|  #define PHY_ID_GPYx15B_MASK	0xFFFFFFFC | ||||
|  #define PHY_ID_GPY21xB_MASK	0xFFFFFFF9 | ||||
| +#define PHY_ID_MXL862XX_MASK	0xFFFFFF00 | ||||
|  #define PHY_ID_GPY2xx		0x67C9DC00 | ||||
|  #define PHY_ID_GPY115B		0x67C9DF00 | ||||
|  #define PHY_ID_GPY115C		0x67C9DF10 | ||||
| @@ -26,6 +27,7 @@ | ||||
|  #define PHY_ID_GPY241B		0x67C9DE40 | ||||
|  #define PHY_ID_GPY241BM		0x67C9DE80 | ||||
|  #define PHY_ID_GPY245B		0x67C9DEC0 | ||||
| +#define PHY_ID_MXL862XX		0xC1335500 | ||||
|   | ||||
|  #define PHY_MIISTAT		0x18	/* MII state */ | ||||
|  #define PHY_IMASK		0x19	/* interrupt mask */ | ||||
| @@ -504,6 +506,15 @@ static int gpy115_loopback(struct phy_device *phydev, bool enable) | ||||
|  	return genphy_soft_reset(phydev); | ||||
|  } | ||||
|   | ||||
| +static int gpy_c45_pma_read_abilities(struct phy_device *phydev) | ||||
| +{ | ||||
| +	phydev->c45_ids.devices_in_package  |= MDIO_DEVS_AN; | ||||
| + | ||||
| +	genphy_c45_pma_read_abilities(phydev); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static struct phy_driver gpy_drivers[] = { | ||||
|  	{ | ||||
|  		PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx), | ||||
| @@ -713,6 +724,22 @@ static struct phy_driver gpy_drivers[] = { | ||||
|  		.get_wol	= gpy_get_wol, | ||||
|  		.set_loopback	= gpy_loopback, | ||||
|  	}, | ||||
| +	{ | ||||
| +		.phy_id		= PHY_ID_MXL862XX, | ||||
| +		.phy_id_mask	= PHY_ID_MXL862XX_MASK, | ||||
| +		.name		= "MaxLinear Ethernet MxL862XX", | ||||
| +		.get_features	= gpy_c45_pma_read_abilities, | ||||
| +		.config_init	= gpy_config_init, | ||||
| +		.probe		= gpy_probe, | ||||
| +		.suspend	= genphy_suspend, | ||||
| +		.resume		= genphy_resume, | ||||
| +		.config_aneg	= gpy_config_aneg, | ||||
| +		.aneg_done	= genphy_c45_aneg_done, | ||||
| +		.read_status	= gpy_read_status, | ||||
| +		.config_intr	= gpy_config_intr, | ||||
| +		.handle_interrupt = gpy_handle_interrupt, | ||||
| +		.set_loopback	= gpy_loopback, | ||||
| +	}, | ||||
|  }; | ||||
|  module_phy_driver(gpy_drivers); | ||||
|   | ||||
| @@ -729,6 +756,7 @@ static struct mdio_device_id __maybe_unused gpy_tbl[] = { | ||||
|  	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)}, | ||||
|  	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)}, | ||||
|  	{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)}, | ||||
| +	{PHY_ID_MXL862XX, PHY_ID_MXL862XX_MASK}, | ||||
|  	{ } | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(mdio, gpy_tbl); | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -0,0 +1,60 @@ | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -5076,4 +5076,5 @@ extern struct net_device *blackhole_netd | ||||
|  		atomic_long_add((VAL), &(DEV)->stats.__##FIELD) | ||||
|  #define DEV_STATS_READ(DEV, FIELD) atomic_long_read(&(DEV)->stats.__##FIELD) | ||||
|   | ||||
| +extern int (*mtk_skb_headroom_copy)(struct sk_buff *new, struct sk_buff *old); | ||||
|  #endif	/* _LINUX_NETDEVICE_H */ | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -198,6 +198,9 @@ static DEFINE_READ_MOSTLY_HASHTABLE(napi | ||||
|   | ||||
|  static DECLARE_RWSEM(devnet_rename_sem); | ||||
|   | ||||
| +int (*mtk_skb_headroom_copy)(struct sk_buff *new, struct sk_buff *old) = NULL; | ||||
| +EXPORT_SYMBOL(mtk_skb_headroom_copy); | ||||
| + | ||||
|  static inline void dev_base_seq_inc(struct net *net) | ||||
|  { | ||||
|  	while (++net->dev_base_seq == 0) | ||||
| @@ -3448,6 +3451,9 @@ static struct sk_buff *validate_xmit_skb | ||||
|  		if (IS_ERR(segs)) { | ||||
|  			goto out_kfree_skb; | ||||
|  		} else if (segs) { | ||||
| +			if (mtk_skb_headroom_copy) | ||||
| +				mtk_skb_headroom_copy(segs, skb); | ||||
| + | ||||
|  			consume_skb(skb); | ||||
|  			skb = segs; | ||||
|  		} | ||||
| --- a/net/ipv4/ip_output.c | ||||
| +++ b/net/ipv4/ip_output.c | ||||
| @@ -705,7 +705,7 @@ struct sk_buff *ip_frag_next(struct sk_b | ||||
|  	} | ||||
|   | ||||
|  	/* Allocate buffer */ | ||||
| -	skb2 = alloc_skb(len + state->hlen + state->ll_rs, GFP_ATOMIC); | ||||
| +	skb2 = alloc_skb(len + state->hlen + state->ll_rs + NET_SKB_PAD + NET_IP_ALIGN, GFP_ATOMIC); | ||||
|  	if (!skb2) | ||||
|  		return ERR_PTR(-ENOMEM); | ||||
|   | ||||
| @@ -714,7 +714,7 @@ struct sk_buff *ip_frag_next(struct sk_b | ||||
|  	 */ | ||||
|   | ||||
|  	ip_copy_metadata(skb2, skb); | ||||
| -	skb_reserve(skb2, state->ll_rs); | ||||
| +	skb_reserve(skb2, state->ll_rs + NET_SKB_PAD + NET_IP_ALIGN); | ||||
|  	skb_put(skb2, len + state->hlen); | ||||
|  	skb_reset_network_header(skb2); | ||||
|  	skb2->transport_header = skb2->network_header + state->hlen; | ||||
| @@ -909,6 +909,9 @@ slow_path: | ||||
|  		} | ||||
|  		ip_frag_ipcb(skb, skb2, first_frag, &state); | ||||
|   | ||||
| +		if (mtk_skb_headroom_copy) | ||||
| +			mtk_skb_headroom_copy(skb2, skb); | ||||
| + | ||||
|  		/* | ||||
|  		 *	Put this fragment into the sending queue. | ||||
|  		 */ | ||||
| @@ -0,0 +1,59 @@ | ||||
| From 214c02d3738a8dee0c07cbf50aa0eb25eed7faa9 Mon Sep 17 00:00:00 2001 | ||||
| From: "chak-kei.lam" <chak-kei.lam@mediatek.com> | ||||
| Date: Fri, 31 Jan 2025 15:15:43 +0800 | ||||
| Subject: [PATCH] net: macvlan: support hardware flow table offload | ||||
|  | ||||
| --- | ||||
|  drivers/net/macvlan.c | 22 ++++++++++++++++++++++ | ||||
|  1 file changed, 22 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c | ||||
| index 90e8b56..2f578a1 100644 | ||||
| --- a/drivers/net/macvlan.c | ||||
| +++ b/drivers/net/macvlan.c | ||||
| @@ -33,6 +33,11 @@ | ||||
|  #include <linux/netpoll.h> | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| +#endif | ||||
| + | ||||
|  #define MACVLAN_HASH_BITS	8 | ||||
|  #define MACVLAN_HASH_SIZE	(1<<MACVLAN_HASH_BITS) | ||||
|  #define MACVLAN_BC_QUEUE_LEN	1000 | ||||
| @@ -1137,6 +1142,20 @@ static int macvlan_dev_get_iflink(const struct net_device *dev) | ||||
|  	return vlan->lowerdev->ifindex; | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int macvlan_dev_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *real_dev = macvlan_dev_real_dev(path->dev); | ||||
| + | ||||
| +	path->dev = real_dev; | ||||
| + | ||||
| +	if (real_dev->netdev_ops->ndo_flow_offload_check) | ||||
| +		return real_dev->netdev_ops->ndo_flow_offload_check(path); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct ethtool_ops macvlan_ethtool_ops = { | ||||
|  	.get_link		= ethtool_op_get_link, | ||||
|  	.get_link_ksettings	= macvlan_ethtool_get_link_ksettings, | ||||
| @@ -1171,6 +1190,9 @@ static const struct net_device_ops macvlan_netdev_ops = { | ||||
|  	.ndo_get_iflink		= macvlan_dev_get_iflink, | ||||
|  	.ndo_features_check	= passthru_features_check, | ||||
|  	.ndo_change_proto_down  = dev_change_proto_down_generic, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check = macvlan_dev_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  void macvlan_common_setup(struct net_device *dev) | ||||
| --  | ||||
| 2.45.2 | ||||
|  | ||||
| @@ -0,0 +1,12 @@ | ||||
| --- a/drivers/net/phy/phy.c | ||||
| +++ b/drivers/net/phy/phy.c | ||||
| @@ -369,7 +369,8 @@ int phy_ethtool_ksettings_set(struct phy | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	if (autoneg == AUTONEG_DISABLE && | ||||
| -	    ((speed != SPEED_1000 && | ||||
| +	    ((speed != SPEED_2500 && | ||||
| +	      speed != SPEED_1000 && | ||||
|  	      speed != SPEED_100 && | ||||
|  	      speed != SPEED_10) || | ||||
|  	     (duplex != DUPLEX_HALF && | ||||
| @@ -0,0 +1,110 @@ | ||||
| diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c | ||||
| index 711f101..d143fdc 100644 | ||||
| --- a/drivers/md/dm-verity-target.c | ||||
| +++ b/drivers/md/dm-verity-target.c | ||||
| @@ -278,6 +278,7 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io, | ||||
|  	int r; | ||||
|  	sector_t hash_block; | ||||
|  	unsigned offset; | ||||
| +	unsigned c; | ||||
|   | ||||
|  	verity_hash_at_level(v, block, level, &hash_block, &offset); | ||||
|   | ||||
| @@ -293,11 +294,23 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io, | ||||
|  			goto release_ret_r; | ||||
|  		} | ||||
|   | ||||
| -		r = verity_hash(v, verity_io_hash_req(v, io), | ||||
| -				data, 1 << v->hash_dev_block_bits, | ||||
| -				verity_io_real_digest(v, io)); | ||||
| -		if (unlikely(r < 0)) | ||||
| -			goto release_ret_r; | ||||
| +		for (c = 0; c < DM_VERITY_MAX_CORRUPTED_ERRS; c++) { | ||||
| +			r = verity_hash(v, verity_io_hash_req(v, io), | ||||
| +					data, 1 << v->hash_dev_block_bits, | ||||
| +					verity_io_real_digest(v, io)); | ||||
| + | ||||
| +			if (unlikely(r < 0)) | ||||
| +				goto release_ret_r; | ||||
| + | ||||
| +			if (likely(memcmp(verity_io_real_digest(v, io), | ||||
| +					  want_digest, | ||||
| +					  v->digest_size) == 0)) | ||||
| +				break; | ||||
| +		} | ||||
| + | ||||
| +		if (c == DM_VERITY_MAX_CORRUPTED_ERRS) | ||||
| +			DMINFO("redo meta hash failure over %d times", | ||||
| +			       DM_VERITY_MAX_CORRUPTED_ERRS); | ||||
|   | ||||
|  		if (likely(memcmp(verity_io_real_digest(v, io), want_digest, | ||||
|  				  v->digest_size) == 0)) | ||||
| @@ -399,6 +412,7 @@ static int verity_for_io_block(struct dm_verity *v, struct dm_verity_io *io, | ||||
|   | ||||
|  		bio_advance_iter(bio, iter, len); | ||||
|  		todo -= len; | ||||
| + | ||||
|  	} while (todo); | ||||
|   | ||||
|  	return 0; | ||||
| @@ -469,7 +483,9 @@ static int verity_verify_io(struct dm_verity_io *io) | ||||
|  	bool is_zero; | ||||
|  	struct dm_verity *v = io->v; | ||||
|  	struct bvec_iter start; | ||||
| +	struct bvec_iter keep; | ||||
|  	unsigned b; | ||||
| +	unsigned c; | ||||
|  	struct crypto_wait wait; | ||||
|   | ||||
|  	for (b = 0; b < io->n_blocks; b++) { | ||||
| @@ -502,19 +518,39 @@ static int verity_verify_io(struct dm_verity_io *io) | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| -		r = verity_hash_init(v, req, &wait); | ||||
| -		if (unlikely(r < 0)) | ||||
| -			return r; | ||||
| - | ||||
|  		start = io->iter; | ||||
| -		r = verity_for_io_block(v, io, &io->iter, &wait); | ||||
| -		if (unlikely(r < 0)) | ||||
| -			return r; | ||||
|   | ||||
| -		r = verity_hash_final(v, req, verity_io_real_digest(v, io), | ||||
| -					&wait); | ||||
| -		if (unlikely(r < 0)) | ||||
| -			return r; | ||||
| +		for (c = 0; c < DM_VERITY_MAX_CORRUPTED_ERRS; c++) { | ||||
| +			keep = start; | ||||
| + | ||||
| +			r = verity_hash_init(v, req, &wait); | ||||
| +			if (unlikely(r < 0)) | ||||
| +				return r; | ||||
| + | ||||
| +			if (c == 0) | ||||
| +				r = verity_for_io_block(v, io, &io->iter, | ||||
| +							&wait); | ||||
| +			else | ||||
| +				r = verity_for_io_block(v, io, &keep, | ||||
| +							&wait); | ||||
| +			if (unlikely(r < 0)) | ||||
| +				return r; | ||||
| + | ||||
| +			r = verity_hash_final(v, req, | ||||
| +					      verity_io_real_digest(v, io), | ||||
| +					      &wait); | ||||
| +			if (unlikely(r < 0)) | ||||
| +				return r; | ||||
| + | ||||
| +			if (likely(memcmp(verity_io_real_digest(v, io), | ||||
| +					  verity_io_want_digest(v, io), | ||||
| +					  v->digest_size) == 0)) | ||||
| +				break; | ||||
| +		} | ||||
| + | ||||
| +		if (c == DM_VERITY_MAX_CORRUPTED_ERRS) | ||||
| +			DMINFO("redo data hash failure over %d times", | ||||
| +			       DM_VERITY_MAX_CORRUPTED_ERRS); | ||||
|   | ||||
|  		if (likely(memcmp(verity_io_real_digest(v, io), | ||||
|  				  verity_io_want_digest(v, io), v->digest_size) == 0)) { | ||||
| @@ -0,0 +1,165 @@ | ||||
| --- a/drivers/md/dm-table.c | ||||
| +++ b/drivers/md/dm-table.c | ||||
| @@ -21,6 +21,7 @@ | ||||
|  #include <linux/blk-mq.h> | ||||
|  #include <linux/mount.h> | ||||
|  #include <linux/dax.h> | ||||
| +#include <linux/blkdev.h> | ||||
|   | ||||
|  #define DM_MSG_PREFIX "table" | ||||
|   | ||||
| @@ -400,6 +401,132 @@ static int upgrade_mode(struct dm_dev_in | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| + * Note for patch conflicts/building failure when upgrading kernel: | ||||
| + * Most of the following code is copied from init/do_mounts.c | ||||
| + */ | ||||
| +struct uuidcmp { | ||||
| +	const char *uuid; | ||||
| +	int len; | ||||
| +}; | ||||
| + | ||||
| +/** | ||||
| + * match_dev_by_uuid - callback for finding a partition using its uuid | ||||
| + * @dev:	device passed in by the caller | ||||
| + * @data:	opaque pointer to the desired struct uuidcmp to match | ||||
| + * | ||||
| + * Returns 1 if the device matches, and 0 otherwise. | ||||
| + */ | ||||
| +static int match_dev_by_uuid(struct device *dev, const void *data) | ||||
| +{ | ||||
| +	const struct uuidcmp *cmp = data; | ||||
| +	struct hd_struct *part = dev_to_part(dev); | ||||
| + | ||||
| +	if (!part->info) | ||||
| +		goto no_match; | ||||
| + | ||||
| +	if (strncasecmp(cmp->uuid, part->info->uuid, cmp->len)) | ||||
| +		goto no_match; | ||||
| + | ||||
| +	return 1; | ||||
| +no_match: | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| + | ||||
| +/** | ||||
| + * devt_from_partuuid - looks up the dev_t of a partition by its UUID | ||||
| + * @uuid_str:	char array containing ascii UUID | ||||
| + * | ||||
| + * The function will return the first partition which contains a matching | ||||
| + * UUID value in its partition_meta_info struct.  This does not search | ||||
| + * by filesystem UUIDs. | ||||
| + * | ||||
| + * If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be | ||||
| + * extracted and used as an offset from the partition identified by the UUID. | ||||
| + * | ||||
| + * Returns the matching dev_t on success or 0 on failure. | ||||
| + */ | ||||
| +static dev_t devt_from_partuuid(const char *uuid_str) | ||||
| +{ | ||||
| +	dev_t res = 0; | ||||
| +	struct uuidcmp cmp; | ||||
| +	struct device *dev = NULL; | ||||
| +	struct gendisk *disk; | ||||
| +	struct hd_struct *part; | ||||
| +	int offset = 0; | ||||
| +	bool prompt_failure = false; | ||||
| +	char *slash; | ||||
| + | ||||
| +	cmp.uuid = uuid_str; | ||||
| + | ||||
| +	slash = strchr(uuid_str, '/'); | ||||
| +	/* Check for optional partition number offset attributes. */ | ||||
| +	if (slash) { | ||||
| +		char c = 0; | ||||
| +		/* Explicitly fail on poor PARTUUID syntax. */ | ||||
| +		if (sscanf(slash + 1, | ||||
| +			   "PARTNROFF=%d%c", &offset, &c) != 1) { | ||||
| +			prompt_failure = true; | ||||
| +			goto done; | ||||
| +		} | ||||
| +		cmp.len = slash - uuid_str; | ||||
| +	} else { | ||||
| +		cmp.len = strlen(uuid_str); | ||||
| +	} | ||||
| + | ||||
| +	if (!cmp.len) { | ||||
| +		prompt_failure = true; | ||||
| +		goto done; | ||||
| +	} | ||||
| + | ||||
| +	dev = class_find_device(&block_class, NULL, &cmp, | ||||
| +				&match_dev_by_uuid); | ||||
| +	if (!dev) | ||||
| +		goto done; | ||||
| + | ||||
| +	res = dev->devt; | ||||
| + | ||||
| +	/* Attempt to find the partition by offset. */ | ||||
| +	if (!offset) | ||||
| +		goto no_offset; | ||||
| + | ||||
| +	res = 0; | ||||
| +	disk = part_to_disk(dev_to_part(dev)); | ||||
| +	part = disk_get_part(disk, dev_to_part(dev)->partno + offset); | ||||
| +	if (part) { | ||||
| +		res = part_devt(part); | ||||
| +		put_device(part_to_dev(part)); | ||||
| +	} | ||||
| + | ||||
| +no_offset: | ||||
| +	put_device(dev); | ||||
| +done: | ||||
| +	if (prompt_failure) { | ||||
| +		pr_err("DM: PARTUUID= is invalid.\n" | ||||
| +		       "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n"); | ||||
| +	} | ||||
| +	return res; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * match_dev_by_label - callback for finding a partition using its label | ||||
| + * @dev:	device passed in by the caller | ||||
| + * @data:	opaque pointer to the label to match | ||||
| + * | ||||
| + * Returns 1 if the device matches, and 0 otherwise. | ||||
| + */ | ||||
| +static int match_dev_by_label(struct device *dev, const void *data) | ||||
| +{ | ||||
| +	const char *label = data; | ||||
| +	struct hd_struct *part = dev_to_part(dev); | ||||
| + | ||||
| +	if (part->info && !strcmp(label, part->info->volname)) | ||||
| +		return 1; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/* | ||||
|   * Convert the path to a device | ||||
|   */ | ||||
|  dev_t dm_get_dev_t(const char *path) | ||||
| @@ -407,6 +534,21 @@ dev_t dm_get_dev_t(const char *path) | ||||
|  	dev_t dev; | ||||
|  	struct block_device *bdev; | ||||
|   | ||||
| +	if (strncmp(path, "PARTUUID=", 9) == 0) { | ||||
| +		return devt_from_partuuid(path + 9); | ||||
| +	} else if (strncmp(path, "PARTLABEL=", 10) == 0) { | ||||
| +		struct device *ddev; | ||||
| + | ||||
| +		ddev = class_find_device(&block_class, NULL, path + 10, | ||||
| +					&match_dev_by_label); | ||||
| +		if (!ddev) | ||||
| +			return 0; | ||||
| + | ||||
| +		dev = ddev->devt; | ||||
| +		put_device(ddev); | ||||
| +		return dev; | ||||
| +	} | ||||
| + | ||||
|  	bdev = lookup_bdev(path); | ||||
|  	if (IS_ERR(bdev)) | ||||
|  		dev = name_to_dev_t(path); | ||||
| @@ -0,0 +1,9 @@ | ||||
| --- a/drivers/md/dm-init.c | ||||
| +++ b/drivers/md/dm-init.c | ||||
| @@ -299,5 +299,5 @@ out: | ||||
|   | ||||
|  late_initcall(dm_init_init); | ||||
|   | ||||
| -module_param(create, charp, 0); | ||||
| +module_param(create, charp, 0444); | ||||
|  MODULE_PARM_DESC(create, "Create a mapped device in early boot"); | ||||
| @@ -1,507 +0,0 @@ | ||||
| From 2d5090dc6072979167593c8fee026341774efb53 Mon Sep 17 00:00:00 2001 | ||||
| From: mtk22468 <Xuzhu.Wang@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 10:50:36 +0800 | ||||
| Subject: [PATCH] ovs add multicast to unicast support | ||||
|  | ||||
| --- | ||||
|  net/openvswitch/actions.c  |  30 ++++ | ||||
|  net/openvswitch/datapath.c | 290 +++++++++++++++++++++++++++++++++++++ | ||||
|  net/openvswitch/datapath.h |  40 +++++ | ||||
|  net/openvswitch/vport.c    |   8 + | ||||
|  net/openvswitch/vport.h    |  26 ++++ | ||||
|  5 files changed, 394 insertions(+) | ||||
|  | ||||
| diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c | ||||
| index 9e8a5c4..82cd46e 100644 | ||||
| --- a/net/openvswitch/actions.c | ||||
| +++ b/net/openvswitch/actions.c | ||||
| @@ -919,6 +919,10 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, | ||||
|  		      struct sw_flow_key *key) | ||||
|  { | ||||
|  	struct vport *vport = ovs_vport_rcu(dp, out_port); | ||||
| +	struct multicast_data_base *mdb; | ||||
| +	struct multicast_table *table; | ||||
| +	struct multicast_table_entry *entry; | ||||
| +	struct sk_buff *skb_cpy; | ||||
|   | ||||
|  	if (likely(vport)) { | ||||
|  		u16 mru = OVS_CB(skb)->mru; | ||||
| @@ -933,6 +937,32 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, | ||||
|   | ||||
|  		if (likely(!mru || | ||||
|  		           (skb->len <= mru + vport->dev->hard_header_len))) { | ||||
| +			if (is_multicast_addr(skb) && !is_igmp_mld(skb) && !is_icmpv6_ndp_rs_ra(skb)) { | ||||
| +				mdb = vport->mdb; | ||||
| +				spin_lock_bh(&mdb->tbl_lock); | ||||
| +				list_for_each_entry(table, &mdb->list_head, mdb_node) { | ||||
| +					if ((key->eth.type == htons(ETH_P_IP) && | ||||
| +						table->group_addr.u.ip4 == key->ipv4.addr.dst) || | ||||
| +						(key->eth.type == htons(ETH_P_IPV6) && | ||||
| +						ipv6_addr_equal(&table->group_addr.u.ip6, &key->ipv6.addr.dst))) { | ||||
| +						list_for_each_entry(entry, &table->entry_list, entry_node) { | ||||
| +							skb_cpy = skb_copy(skb, GFP_ATOMIC); | ||||
| +							if (!skb_cpy) { | ||||
| +								kfree_skb(skb); | ||||
| +								pr_err("%s(): skb copy error\n", __func__); | ||||
| +								spin_unlock_bh(&mdb->tbl_lock); | ||||
| +								return; | ||||
| +							} | ||||
| +							memcpy(skb_cpy->data, entry->eth_addr, ETH_ALEN); | ||||
| +							ovs_vport_send(vport, skb_cpy, ovs_key_mac_proto(key)); | ||||
| +						} | ||||
| +						spin_unlock_bh(&mdb->tbl_lock); | ||||
| +						kfree_skb(skb); | ||||
| +						return; | ||||
| +					} | ||||
| +				} | ||||
| +				spin_unlock_bh(&mdb->tbl_lock); | ||||
| +			} | ||||
|  			ovs_vport_send(vport, skb, ovs_key_mac_proto(key)); | ||||
|  		} else if (mru <= vport->dev->mtu) { | ||||
|  			struct net *net = read_pnet(&dp->net); | ||||
| diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c | ||||
| index 4c537e7..0c8d344 100644 | ||||
| --- a/net/openvswitch/datapath.c | ||||
| +++ b/net/openvswitch/datapath.c | ||||
| @@ -11,6 +11,9 @@ | ||||
|  #include <linux/if_vlan.h> | ||||
|  #include <linux/in.h> | ||||
|  #include <linux/ip.h> | ||||
| +#include <linux/igmp.h> | ||||
| +#include <net/mld.h> | ||||
| +#include <linux/icmpv6.h> | ||||
|  #include <linux/jhash.h> | ||||
|  #include <linux/delay.h> | ||||
|  #include <linux/time.h> | ||||
| @@ -538,6 +541,271 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +static int ovs_multicast_add_group(struct ip_addr *_group_addr, | ||||
| +									const u8 *entry_addr, | ||||
| +									struct vport *input_vport) | ||||
| +{ | ||||
| +	struct multicast_data_base *mdb; | ||||
| +	struct multicast_table *table; | ||||
| +	struct multicast_table_entry *entry; | ||||
| +	int err; | ||||
| + | ||||
| +	mdb = input_vport->mdb; | ||||
| +	spin_lock_bh(&mdb->tbl_lock); | ||||
| +	list_for_each_entry(table, &mdb->list_head, mdb_node) { | ||||
| +		if (!memcmp(&table->group_addr.u, &_group_addr->u, sizeof(struct ip_addr))) { | ||||
| +			list_for_each_entry(entry, &table->entry_list, entry_node) { | ||||
| +				if (ether_addr_equal(entry->eth_addr, entry_addr)) | ||||
| +					goto out; | ||||
| +			} | ||||
| + | ||||
| +			entry = kzalloc(sizeof(struct multicast_table_entry), GFP_ATOMIC); | ||||
| +			if (!entry) { | ||||
| +				err = -ENOMEM; | ||||
| +				goto err; | ||||
| +			} | ||||
| + | ||||
| +			memcpy(entry->eth_addr, entry_addr, ETH_ALEN); | ||||
| +			list_add(&entry->entry_node, &table->entry_list); | ||||
| +			goto out; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	table = kzalloc(sizeof(struct multicast_table), GFP_ATOMIC); | ||||
| +	if (!table) { | ||||
| +		err = -ENOMEM; | ||||
| +		goto err; | ||||
| +	} | ||||
| + | ||||
| +	INIT_LIST_HEAD(&table->entry_list); | ||||
| +	entry = kzalloc(sizeof(struct multicast_table_entry), GFP_ATOMIC); | ||||
| +	if (!entry) { | ||||
| +		kfree(table); | ||||
| +		err = -ENOMEM; | ||||
| +		goto err; | ||||
| +	} | ||||
| + | ||||
| +	memcpy(entry->eth_addr, entry_addr, ETH_ALEN); | ||||
| +	list_add(&entry->entry_node, &table->entry_list); | ||||
| + | ||||
| +	table->group_addr.u = _group_addr->u; | ||||
| +	list_add(&table->mdb_node, &mdb->list_head); | ||||
| + | ||||
| +out: | ||||
| +	err = 0; | ||||
| +err: | ||||
| +	spin_unlock_bh(&mdb->tbl_lock); | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int ovs_multicast_leave_group(struct ip_addr *_group_addr, | ||||
| +									const u8 *entry_addr, | ||||
| +									struct vport *input_vport) | ||||
| +{ | ||||
| +	struct multicast_data_base *mdb; | ||||
| +	struct multicast_table *table, *table_tmp; | ||||
| +	struct multicast_table_entry *entry, *entry_tmp; | ||||
| +	int err; | ||||
| + | ||||
| +	mdb = input_vport->mdb; | ||||
| +	spin_lock_bh(&mdb->tbl_lock); | ||||
| +	list_for_each_entry_safe(table, table_tmp, &mdb->list_head, mdb_node) { | ||||
| +		if (!memcmp(&table->group_addr.u, &_group_addr->u, sizeof(struct ip_addr))) { | ||||
| +			list_for_each_entry_safe(entry, entry_tmp, &table->entry_list, entry_node) { | ||||
| +				if (ether_addr_equal(entry->eth_addr, entry_addr)) { | ||||
| +					list_del(&entry->entry_node); | ||||
| +					kfree(entry); | ||||
| + | ||||
| +					if (list_empty(&table->entry_list)) { | ||||
| +						list_del(&table->mdb_node); | ||||
| +						kfree(table); | ||||
| +					} | ||||
| + | ||||
| +					goto out; | ||||
| +				} | ||||
| +			} | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +out: | ||||
| +	err = 0; | ||||
| +	spin_unlock_bh(&mdb->tbl_lock); | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int ovs_multicast_ipv4_rcv(struct sk_buff *skb, struct vport *input_vport) | ||||
| +{ | ||||
| +	struct ethhdr *eth_hdr; | ||||
| +	const u8 *dl_src; | ||||
| +	struct ip_addr group_addr = {0}; | ||||
| +	struct iphdr *ip_header; | ||||
| +	struct igmphdr *igmp_header; | ||||
| +	int i; | ||||
| +	struct igmpv3_report *igmpv3_hdr; | ||||
| +	u16 group_num; | ||||
| +	struct igmpv3_grec *grec; | ||||
| +	u8 group_type; | ||||
| +	u8 aux_data_len; | ||||
| +	u16 num_of_source; | ||||
| +	int err; | ||||
| + | ||||
| +	err = ip_mc_check_igmp(skb); | ||||
| +	if (err) | ||||
| +		return 0; | ||||
| + | ||||
| +	eth_hdr = skb_eth_hdr(skb); | ||||
| +	dl_src = eth_hdr->h_source; | ||||
| +	ip_header = ip_hdr(skb); | ||||
| +	igmp_header = igmp_hdr(skb); | ||||
| + | ||||
| +	switch (igmp_header->type) { | ||||
| +	case IGMP_HOST_MEMBERSHIP_REPORT: | ||||
| +	case IGMPV2_HOST_MEMBERSHIP_REPORT: | ||||
| +		group_addr.u.ip4 = igmp_header->group; | ||||
| +		if (ipv4_is_local_multicast(group_addr.u.ip4)) | ||||
| +			return 0; | ||||
| +		ovs_multicast_add_group(&group_addr, dl_src, input_vport); | ||||
| +		break; | ||||
| +	case IGMP_HOST_LEAVE_MESSAGE: | ||||
| +		group_addr.u.ip4 = igmp_header->group; | ||||
| +		if (ipv4_is_local_multicast(group_addr.u.ip4)) | ||||
| +			return 0; | ||||
| +		ovs_multicast_leave_group(&group_addr, dl_src, input_vport); | ||||
| +		break; | ||||
| +	case IGMPV3_HOST_MEMBERSHIP_REPORT: | ||||
| +		igmpv3_hdr = (struct igmpv3_report *)igmp_header; | ||||
| +		group_num = ntohs(igmpv3_hdr->ngrec); | ||||
| +		grec = igmpv3_hdr->grec; | ||||
| + | ||||
| +		for (i = 0; i < group_num; i++) { | ||||
| +			group_type = grec->grec_type; | ||||
| +			aux_data_len = grec->grec_auxwords; | ||||
| +			num_of_source = ntohs(grec->grec_nsrcs); | ||||
| +			group_addr.u.ip4 = grec->grec_mca; | ||||
| +			if (ipv4_is_local_multicast(group_addr.u.ip4)) | ||||
| +				return 0; | ||||
| + | ||||
| +			if (group_type == IGMPV3_MODE_IS_EXCLUDE || | ||||
| +				group_type == IGMPV3_CHANGE_TO_EXCLUDE || | ||||
| +				group_type == IGMPV3_ALLOW_NEW_SOURCES) | ||||
| +				ovs_multicast_add_group(&group_addr, dl_src, input_vport); | ||||
| + | ||||
| +			if (group_type == IGMPV3_MODE_IS_INCLUDE || | ||||
| +				group_type == IGMPV3_CHANGE_TO_INCLUDE || | ||||
| +				group_type == IGMPV3_BLOCK_OLD_SOURCES) | ||||
| +				if (num_of_source == 0) | ||||
| +					ovs_multicast_leave_group(&group_addr, dl_src, input_vport); | ||||
| + | ||||
| +			grec = (struct igmpv3_grec *)((u8 *)grec + sizeof(struct igmpv3_grec) | ||||
| +					+ aux_data_len * sizeof(u32)); | ||||
| +		} | ||||
| +		break; | ||||
| +	case IGMP_HOST_MEMBERSHIP_QUERY: | ||||
| +		break; | ||||
| +	default: | ||||
| +		pr_warning("%s(): error packet type 0x%x\n", __func__, igmp_header->type); | ||||
| +		break; | ||||
| +	} | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int ovs_multicast_ipv6_rcv(struct sk_buff *skb, struct vport *input_vport) | ||||
| +{ | ||||
| +	const u8 *dl_src; | ||||
| +	struct mld_msg *mld_hdr; | ||||
| +	struct ip_addr group_addr = {0}; | ||||
| +	struct icmp6hdr *icmpv6_hdr; | ||||
| +	u16 group_num; | ||||
| +	struct mld2_grec *grec; | ||||
| +	u8 group_type; | ||||
| +	u8 aux_data_len; | ||||
| +	u16 num_of_source; | ||||
| +	int i; | ||||
| +	int err; | ||||
| + | ||||
| +	err = ipv6_mc_check_mld(skb); | ||||
| +	if (err) | ||||
| +		return err; | ||||
| + | ||||
| +	mld_hdr = (struct mld_msg *)skb_transport_header(skb); | ||||
| +	dl_src = skb_eth_hdr(skb)->h_source; | ||||
| + | ||||
| +	switch (mld_hdr->mld_type) { | ||||
| +	case ICMPV6_MGM_REPORT: | ||||
| +		group_addr.u.ip6 = mld_hdr->mld_mca; | ||||
| +		if (ipv6_addr_is_ll_all_nodes(&group_addr.u.ip6)) | ||||
| +			return 0; | ||||
| +		ovs_multicast_add_group(&group_addr, dl_src, input_vport); | ||||
| +		break; | ||||
| +	case ICMPV6_MGM_REDUCTION: | ||||
| +		group_addr.u.ip6 = mld_hdr->mld_mca; | ||||
| +		if (ipv6_addr_is_ll_all_nodes(&group_addr.u.ip6)) | ||||
| +			return 0; | ||||
| +		ovs_multicast_leave_group(&group_addr, dl_src, input_vport); | ||||
| +		break; | ||||
| +	case ICMPV6_MLD2_REPORT: | ||||
| +		icmpv6_hdr = icmp6_hdr(skb); | ||||
| +		group_num = ntohs(icmpv6_hdr->icmp6_dataun.un_data16[1]); | ||||
| +		grec = (struct mld2_grec *)(skb_transport_header(skb) + sizeof(struct icmp6hdr)); | ||||
| + | ||||
| +		for (i = 0; i < group_num; i++) { | ||||
| +			group_type = grec->grec_type; | ||||
| +			aux_data_len = grec->grec_auxwords; | ||||
| +			num_of_source = ntohs(grec->grec_nsrcs); | ||||
| +			group_addr.u.ip6 = grec->grec_mca; | ||||
| +			if (ipv6_addr_is_ll_all_nodes(&group_addr.u.ip6)) | ||||
| +				return 0; | ||||
| + | ||||
| +			if ((group_type == MLD2_MODE_IS_EXCLUDE || | ||||
| +				group_type == MLD2_CHANGE_TO_EXCLUDE || | ||||
| +				group_type == MLD2_ALLOW_NEW_SOURCES) && | ||||
| +				num_of_source == 0) | ||||
| +				ovs_multicast_add_group(&group_addr, dl_src, input_vport); | ||||
| +			else if ((group_type == MLD2_MODE_IS_INCLUDE || | ||||
| +					group_type == MLD2_CHANGE_TO_INCLUDE || | ||||
| +					group_type == MLD2_BLOCK_OLD_SOURCES) && | ||||
| +					num_of_source == 0) | ||||
| +				ovs_multicast_leave_group(&group_addr, dl_src, input_vport); | ||||
| +			else { | ||||
| +				pr_info("%s(): group_type(%d), aux_data_len(%d),\ | ||||
| +						num_of_source(%d), group_addr(%pI6)\n", | ||||
| +						__func__, group_type, aux_data_len, | ||||
| +						num_of_source, &group_addr.u.ip6); | ||||
| +				return 0; | ||||
| +			} | ||||
| +			grec = (struct mld2_grec *)((u8 *)grec + sizeof(struct mld2_grec) | ||||
| +										+ aux_data_len * sizeof(u32)); | ||||
| +		} | ||||
| +		break; | ||||
| +	case ICMPV6_MGM_QUERY: | ||||
| +		break; | ||||
| +	default: | ||||
| +		pr_warning("%s(): error packet type 0x%x\n", __func__, mld_hdr->mld_type); | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int ovs_multicast_rcv(struct sk_buff *skb, struct vport *input_vport) | ||||
| +{ | ||||
| +	int ret = 0; | ||||
| + | ||||
| +	if (!skb) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	switch (skb->protocol) { | ||||
| +	case htons(ETH_P_IP): | ||||
| +		ret = ovs_multicast_ipv4_rcv(skb, input_vport); | ||||
| +		break; | ||||
| +	case htons(ETH_P_IPV6): | ||||
| +		ret = ovs_multicast_ipv6_rcv(skb, input_vport); | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) | ||||
|  { | ||||
|  	struct ovs_header *ovs_header = info->userhdr; | ||||
| @@ -612,6 +880,9 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) | ||||
|  	OVS_CB(packet)->input_vport = input_vport; | ||||
|  	sf_acts = rcu_dereference(flow->sf_acts); | ||||
|   | ||||
| +	if (is_multicast_addr(packet) && !is_icmpv6_ndp_rs_ra(packet)) | ||||
| +		ovs_multicast_rcv(packet, input_vport); | ||||
| + | ||||
|  	local_bh_disable(); | ||||
|  	err = ovs_execute_actions(dp, packet, sf_acts, &flow->key); | ||||
|  	local_bh_enable(); | ||||
| @@ -2199,6 +2470,9 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) | ||||
|  	struct datapath *dp; | ||||
|  	struct vport *vport; | ||||
|  	unsigned int new_headroom; | ||||
| +	struct multicast_data_base *mdb; | ||||
| +	struct multicast_table *table, *table_tmp; | ||||
| +	struct multicast_table_entry *entry, *entry_tmp; | ||||
|  	int err; | ||||
|   | ||||
|  	reply = ovs_vport_cmd_alloc_info(); | ||||
| @@ -2226,6 +2500,22 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) | ||||
|  	if (netdev_get_fwd_headroom(vport->dev) == dp->max_headroom) | ||||
|  		update_headroom = true; | ||||
|   | ||||
| +	mdb = vport->mdb; | ||||
| +	spin_lock_bh(&mdb->tbl_lock); | ||||
| +	list_for_each_entry_safe(table, table_tmp, &mdb->list_head, mdb_node) { | ||||
| +		list_for_each_entry_safe(entry, entry_tmp, &table->entry_list, entry_node) { | ||||
| +			list_del(&entry->entry_node); | ||||
| +			kfree(entry); | ||||
| + | ||||
| +			if (list_empty(&table->entry_list)) { | ||||
| +				list_del(&table->mdb_node); | ||||
| +				kfree(table); | ||||
| +			} | ||||
| +		} | ||||
| +	} | ||||
| +	spin_unlock_bh(&mdb->tbl_lock); | ||||
| +	kfree(mdb); | ||||
| + | ||||
|  	netdev_reset_rx_headroom(vport->dev); | ||||
|  	ovs_dp_detach_port(vport); | ||||
|   | ||||
| diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h | ||||
| index 81e85dd..50596bc 100644 | ||||
| --- a/net/openvswitch/datapath.h | ||||
| +++ b/net/openvswitch/datapath.h | ||||
| @@ -215,6 +215,46 @@ static inline struct datapath *get_dp(struct net *net, int dp_ifindex) | ||||
|  	return dp; | ||||
|  } | ||||
|   | ||||
| +static inline bool is_multicast_addr(struct sk_buff *skb) | ||||
| +{ | ||||
| +	struct ethhdr *eth_hdr; | ||||
| + | ||||
| +	if (!skb) | ||||
| +		return 0; | ||||
| + | ||||
| +	eth_hdr = skb_eth_hdr(skb); | ||||
| + | ||||
| +	return (eth_hdr->h_dest[0] == 0x01 && skb->protocol == htons(ETH_P_IP)) || | ||||
| +			(eth_hdr->h_dest[0] == 0x33 && skb->protocol == htons(ETH_P_IPV6)); | ||||
| +} | ||||
| + | ||||
| +static inline bool is_igmp_mld(struct sk_buff *skb) | ||||
| +{ | ||||
| +	struct ethhdr *eth_hdr; | ||||
| +	int err = 0; | ||||
| + | ||||
| +	if (!skb) | ||||
| +		return err; | ||||
| + | ||||
| +	eth_hdr = skb_eth_hdr(skb); | ||||
| + | ||||
| +	if (skb->protocol == htons(ETH_P_IP)) { | ||||
| +		err = ip_hdr(skb)->protocol == IPPROTO_IGMP; | ||||
| +	} else if (skb->protocol == htons(ETH_P_IPV6)) { | ||||
| +		err = !ipv6_mc_check_mld(skb); | ||||
| +	} | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static inline bool is_icmpv6_ndp_rs_ra(struct sk_buff *skb) | ||||
| +{ | ||||
| +	if (!skb) | ||||
| +		return 0; | ||||
| + | ||||
| +	return ((icmp6_hdr(skb)->icmp6_type == NDISC_ROUTER_SOLICITATION) && (icmp6_hdr(skb)->icmp6_type == NDISC_ROUTER_ADVERTISEMENT)); | ||||
| +} | ||||
| + | ||||
|  extern struct notifier_block ovs_dp_device_notifier; | ||||
|  extern struct genl_family dp_vport_genl_family; | ||||
|   | ||||
| diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c | ||||
| index 19af0ef..77bc923 100644 | ||||
| --- a/net/openvswitch/vport.c | ||||
| +++ b/net/openvswitch/vport.c | ||||
| @@ -141,6 +141,14 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, | ||||
|  		return ERR_PTR(-EINVAL); | ||||
|  	} | ||||
|   | ||||
| +	vport->mdb = kzalloc(sizeof(struct multicast_data_base), GFP_KERNEL); | ||||
| +	if (!vport->mdb) { | ||||
| +		kfree(vport); | ||||
| +		return ERR_PTR(-ENOMEM); | ||||
| +	} | ||||
| +	INIT_LIST_HEAD(&vport->mdb->list_head); | ||||
| +	spin_lock_init(&vport->mdb->tbl_lock); | ||||
| + | ||||
|  	return vport; | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(ovs_vport_alloc); | ||||
| diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h | ||||
| index 1eb7495..eb69d6c 100644 | ||||
| --- a/net/openvswitch/vport.h | ||||
| +++ b/net/openvswitch/vport.h | ||||
| @@ -55,6 +55,30 @@ struct vport_portids { | ||||
|  	u32 ids[]; | ||||
|  }; | ||||
|   | ||||
| +struct ip_addr { | ||||
| +	union { | ||||
| +		__be32 ip4; | ||||
| +		struct in6_addr ip6; | ||||
| +	} u; | ||||
| +}; | ||||
| + | ||||
| +struct multicast_table_entry { | ||||
| +	struct list_head entry_node; | ||||
| +	u8 eth_addr[ETH_ALEN]; | ||||
| +}; | ||||
| + | ||||
| +struct multicast_table { | ||||
| +	struct list_head mdb_node; | ||||
| +	struct list_head entry_list; | ||||
| +	struct ip_addr group_addr; | ||||
| +}; | ||||
| + | ||||
| +struct multicast_data_base { | ||||
| +	struct list_head list_head; | ||||
| +	spinlock_t tbl_lock; | ||||
| +}; | ||||
| + | ||||
| + | ||||
|  /** | ||||
|   * struct vport - one port within a datapath | ||||
|   * @dev: Pointer to net_device. | ||||
| @@ -79,6 +103,8 @@ struct vport { | ||||
|   | ||||
|  	struct list_head detach_list; | ||||
|  	struct rcu_head rcu; | ||||
| + | ||||
| +	struct multicast_data_base *mdb; | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,181 +0,0 @@ | ||||
| From f99c7b63e766c2ff8851a8ba6ff77f3d8bfef0d5 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo Jiao <Bo.Jiao@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 10:55:08 +0800 | ||||
| Subject: [PATCH 02/24] dts netsys2 wed changes | ||||
|  | ||||
| --- | ||||
|  .../boot/dts/mediatek/mt7981-spim-nor-rfb.dts |  8 ----- | ||||
|  arch/arm64/boot/dts/mediatek/mt7981.dtsi      | 21 ++++-------- | ||||
|  arch/arm64/boot/dts/mediatek/mt7986a.dtsi     | 33 +++++++------------ | ||||
|  arch/arm64/boot/dts/mediatek/mt7986b.dtsi     | 33 +++++++------------ | ||||
|  4 files changed, 28 insertions(+), 67 deletions(-) | ||||
|  | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts b/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts | ||||
| index 3fa55a0..f5c70a4 100755 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts | ||||
| @@ -211,11 +211,3 @@ | ||||
|  &xhci {   | ||||
|          status = "okay"; | ||||
|  }; | ||||
| - | ||||
| -&wed { | ||||
| -	dy_txbm_enable = "true"; | ||||
| -	dy_txbm_budget = <8>; | ||||
| -	txbm_init_sz = <8>; | ||||
| -	txbm_max_sz = <32>; | ||||
| -	status = "okay"; | ||||
| -}; | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7981.dtsi b/arch/arm64/boot/dts/mediatek/mt7981.dtsi | ||||
| index 91415e4..283421a 100644 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7981.dtsi | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7981.dtsi | ||||
| @@ -90,22 +90,12 @@ | ||||
|  		#io-channel-cells = <1>; | ||||
|  	}; | ||||
|   | ||||
| -	wed: wed@15010000 { | ||||
| -		compatible = "mediatek,wed"; | ||||
| -		wed_num = <2>; | ||||
| -		/* add this property for wed get the pci slot number. */ | ||||
| -		pci_slot_map = <0>, <1>; | ||||
| -		reg = <0 0x15010000 0 0x1000>, | ||||
| -		      <0 0x15011000 0 0x1000>; | ||||
| +	wed0: wed@15010000 { | ||||
| +		compatible = "mediatek,mt7981-wed", | ||||
| +			     "syscon"; | ||||
| +		reg = <0 0x15010000 0 0x1000>; | ||||
|  		interrupt-parent = <&gic>; | ||||
| -		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>, | ||||
| -			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>; | ||||
| -	}; | ||||
| - | ||||
| -	wdma: wdma@15104800 { | ||||
| -		compatible = "mediatek,wed-wdma"; | ||||
| -		reg = <0 0x15104800 0 0x400>, | ||||
| -		      <0 0x15104c00 0 0x400>; | ||||
| +		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>; | ||||
|  	}; | ||||
|   | ||||
|  	ap2woccif: ap2woccif@151A5000 { | ||||
| @@ -423,6 +413,7 @@ | ||||
|                  mediatek,ethsys = <ðsys>; | ||||
|  		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; | ||||
|  		mediatek,infracfg = <&topmisc>; | ||||
| +		mediatek,wed = <&wed0>; | ||||
|                  #reset-cells = <1>; | ||||
|                  #address-cells = <1>; | ||||
|                  #size-cells = <0>; | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi | ||||
| index 2c7e171..3a4f279 100644 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi | ||||
| @@ -58,32 +58,20 @@ | ||||
|  		}; | ||||
|  	}; | ||||
|   | ||||
| -	wed: wed@15010000 { | ||||
| -		compatible = "mediatek,wed"; | ||||
| -		wed_num = <2>; | ||||
| -		/* add this property for wed get the pci slot number. */ | ||||
| -		pci_slot_map = <0>, <1>; | ||||
| -		reg = <0 0x15010000 0 0x1000>, | ||||
| -		      <0 0x15011000 0 0x1000>; | ||||
| +	wed0: wed@15010000 { | ||||
| +		compatible = "mediatek,mt7986-wed", | ||||
| +			     "syscon"; | ||||
| +		reg = <0 0x15010000 0 0x1000>; | ||||
|  		interrupt-parent = <&gic>; | ||||
| -		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>, | ||||
| -			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>; | ||||
| +		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>; | ||||
|  	}; | ||||
|   | ||||
| -	wed2: wed2@15011000 { | ||||
| -		compatible = "mediatek,wed2"; | ||||
| -		wed_num = <2>; | ||||
| -		reg = <0 0x15010000 0 0x1000>, | ||||
| -		      <0 0x15011000 0 0x1000>; | ||||
| +	wed1: wed@15011000 { | ||||
| +		compatible = "mediatek,mt7986-wed", | ||||
| +			     "syscon"; | ||||
| +		reg = <0 0x15011000 0 0x1000>; | ||||
|  		interrupt-parent = <&gic>; | ||||
| -		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>, | ||||
| -			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>; | ||||
| -	}; | ||||
| - | ||||
| -	wdma: wdma@15104800 { | ||||
| -		compatible = "mediatek,wed-wdma"; | ||||
| -		reg = <0 0x15104800 0 0x400>, | ||||
| -		      <0 0x15104c00 0 0x400>; | ||||
| +		interrupts = <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>; | ||||
|  	}; | ||||
|   | ||||
|  	ap2woccif: ap2woccif@151A5000 { | ||||
| @@ -494,6 +482,7 @@ | ||||
|  					 <&topckgen CK_TOP_CB_SGM_325M>; | ||||
|                  mediatek,ethsys = <ðsys>; | ||||
|  		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; | ||||
| +		mediatek,wed = <&wed0>, <&wed1>; | ||||
|                  #reset-cells = <1>; | ||||
|                  #address-cells = <1>; | ||||
|                  #size-cells = <0>; | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7986b.dtsi b/arch/arm64/boot/dts/mediatek/mt7986b.dtsi | ||||
| index 26f093b..ce884f0 100644 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7986b.dtsi | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7986b.dtsi | ||||
| @@ -58,32 +58,20 @@ | ||||
|  		}; | ||||
|  	}; | ||||
|   | ||||
| -	wed: wed@15010000 { | ||||
| -		compatible = "mediatek,wed"; | ||||
| -		wed_num = <2>; | ||||
| -		/* add this property for wed get the pci slot number. */ | ||||
| -		pci_slot_map = <0>, <1>; | ||||
| -		reg = <0 0x15010000 0 0x1000>, | ||||
| -		      <0 0x15011000 0 0x1000>; | ||||
| +	wed0: wed@15010000 { | ||||
| +		compatible = "mediatek,mt7986-wed", | ||||
| +			     "syscon"; | ||||
| +		reg = <0 0x15010000 0 0x1000>; | ||||
|  		interrupt-parent = <&gic>; | ||||
| -		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>, | ||||
| -			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>; | ||||
| +		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>; | ||||
|  	}; | ||||
|   | ||||
| -	wed2: wed2@15011000 { | ||||
| -		compatible = "mediatek,wed2"; | ||||
| -		wed_num = <2>; | ||||
| -		reg = <0 0x15010000 0 0x1000>, | ||||
| -		      <0 0x15011000 0 0x1000>; | ||||
| +	wed1: wed@15011000 { | ||||
| +		compatible = "mediatek,mt7986-wed", | ||||
| +			     "syscon"; | ||||
| +		reg = <0 0x15011000 0 0x1000>; | ||||
|  		interrupt-parent = <&gic>; | ||||
| -		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>, | ||||
| -			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>; | ||||
| -	}; | ||||
| - | ||||
| -	wdma: wdma@15104800 { | ||||
| -		compatible = "mediatek,wed-wdma"; | ||||
| -		reg = <0 0x15104800 0 0x400>, | ||||
| -		      <0 0x15104c00 0 0x400>; | ||||
| +		interrupts = <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>; | ||||
|  	}; | ||||
|   | ||||
|  	ap2woccif: ap2woccif@151A5000 { | ||||
| @@ -408,6 +396,7 @@ | ||||
|  					 <&topckgen CK_TOP_CB_SGM_325M>; | ||||
|                  mediatek,ethsys = <ðsys>; | ||||
|  		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; | ||||
| +		mediatek,wed = <&wed0>, <&wed1>; | ||||
|                  #reset-cells = <1>; | ||||
|                  #address-cells = <1>; | ||||
|                  #size-cells = <0>; | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,354 +0,0 @@ | ||||
| From 07a3ac0ae724c4df67316e01b03432d8ee9f4229 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Mar 2024 17:56:01 +0800 | ||||
| Subject: [PATCH 04/24] ethernet update ppe from netsys1 to netsys2 | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c   | 19 ++++--- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  7 ++- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c       | 29 +++++++++-- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.h       | 51 +++++++++++++++++++ | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   |  8 +++ | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe_regs.h  | 10 ++++ | ||||
|  6 files changed, 112 insertions(+), 12 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index ffaa515..9262227 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -2447,16 +2447,20 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, | ||||
|  			skb_checksum_none_assert(skb); | ||||
|  		skb->protocol = eth_type_trans(skb, netdev); | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) | ||||
| +		hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY_V2; | ||||
| +		reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON_V2, trxd.rxd5); | ||||
| +		if (hash != MTK_RXD5_FOE_ENTRY_V2) | ||||
| +			skb_set_hash(skb, jhash_1word(hash, 0), PKT_HASH_TYPE_L4); | ||||
| +#else | ||||
|  		hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; | ||||
| -		if (hash != MTK_RXD4_FOE_ENTRY) { | ||||
| -			hash = jhash_1word(hash, 0); | ||||
| -			skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); | ||||
| -		} | ||||
| - | ||||
|  		reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); | ||||
| +		if (hash != MTK_RXD4_FOE_ENTRY) | ||||
| +			skb_set_hash(skb, jhash_1word(hash, 0), PKT_HASH_TYPE_L4); | ||||
| +#endif | ||||
| + | ||||
|  		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) | ||||
| -			mtk_ppe_check_skb(eth->ppe, skb, | ||||
| -					  trxd.rxd4 & MTK_RXD4_FOE_ENTRY); | ||||
| +			mtk_ppe_check_skb(eth->ppe, skb, hash); | ||||
|   | ||||
|  		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { | ||||
|  			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { | ||||
| @@ -5957,6 +5961,7 @@ static const struct mtk_soc_data mt7986_data = { | ||||
|  	.required_clks = MT7986_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.offload_version = 2, | ||||
|  	.rss_num = 4, | ||||
|  	.txrx = { | ||||
|  		.txd_size = sizeof(struct mtk_tx_dma_v2), | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index 41daeb2..910baaf 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -137,7 +137,7 @@ | ||||
|  #define MTK_GDMA_UCS_EN		BIT(20) | ||||
|  #define MTK_GDMA_STRP_CRC	BIT(16) | ||||
|  #define MTK_GDMA_TO_PDMA	0x0 | ||||
| -#define MTK_GDMA_TO_PPE		0x4444 | ||||
| +#define MTK_GDMA_TO_PPE		0x3333 | ||||
|  #define MTK_GDMA_DROP_ALL	0x7777 | ||||
|   | ||||
|  /* GDM Egress Control Register */ | ||||
| @@ -680,6 +680,11 @@ | ||||
|  #define MTK_RXD4_SRC_PORT	GENMASK(21, 19) | ||||
|  #define MTK_RXD4_ALG		GENMASK(31, 22) | ||||
|   | ||||
| +/* QDMA descriptor rxd4 */ | ||||
| +#define MTK_RXD5_FOE_ENTRY_V2	GENMASK(14, 0) | ||||
| +#define MTK_RXD5_PPE_CPU_REASON_V2	GENMASK(22, 18) | ||||
| +#define MTK_RXD5_SRC_PORT_V2	GENMASK(29, 26) | ||||
| + | ||||
|  /* QDMA descriptor rxd4 */ | ||||
|  #define RX_DMA_L4_VALID		BIT(24) | ||||
|  #define RX_DMA_L4_VALID_PDMA	BIT(30)		/* when PDMA is used */ | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index eeaec1b..e195fb3 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -122,7 +122,7 @@ static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e) | ||||
|  	hash = (hash >> 24) | ((hash & 0xffffff) << 8); | ||||
|  	hash ^= hv1 ^ hv2 ^ hv3; | ||||
|  	hash ^= hash >> 16; | ||||
| -	hash <<= 1; | ||||
| +	hash <<= 2; | ||||
|  	hash &= MTK_PPE_ENTRIES - 1; | ||||
|   | ||||
|  	return hash; | ||||
| @@ -171,8 +171,12 @@ int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, | ||||
|  	      MTK_FOE_IB1_BIND_CACHE; | ||||
|  	entry->ib1 = val; | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	val = FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0xf) | | ||||
| +#else | ||||
|  	val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | | ||||
|  	      FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) | | ||||
| +#endif | ||||
|  	      FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port); | ||||
|   | ||||
|  	if (is_multicast_ether_addr(dest_mac)) | ||||
| @@ -359,12 +363,19 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|   | ||||
|  	*ib2 &= ~MTK_FOE_IB2_PORT_MG; | ||||
|  	*ib2 |= MTK_FOE_IB2_WDMA_WINFO; | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	*ib2 |=  FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq); | ||||
| + | ||||
| +	l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) | | ||||
| +		    FIELD_PREP(MTK_FOE_WINFO_BSS, bss); | ||||
| +#else | ||||
|  	if (wdma_idx) | ||||
|  		*ib2 |= MTK_FOE_IB2_WDMA_DEVIDX; | ||||
|   | ||||
|  	l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) | | ||||
|  		    FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) | | ||||
|  		    FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq); | ||||
| +#endif | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -746,6 +757,9 @@ int mtk_ppe_start(struct mtk_ppe *ppe) | ||||
|  	      MTK_PPE_TB_CFG_AGE_TCP | | ||||
|  	      MTK_PPE_TB_CFG_AGE_UDP | | ||||
|  	      MTK_PPE_TB_CFG_AGE_TCP_FIN | | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	      MTK_PPE_TB_CFG_INFO_SEL | | ||||
| +#endif | ||||
|  	      FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS, | ||||
|  			 MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) | | ||||
|  	      FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE, | ||||
| @@ -762,15 +776,17 @@ int mtk_ppe_start(struct mtk_ppe *ppe) | ||||
|   | ||||
|  	mtk_ppe_cache_enable(ppe, true); | ||||
|   | ||||
| -	val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG | | ||||
| -	      MTK_PPE_FLOW_CFG_IP4_UDP_FRAG | | ||||
| +	val = MTK_PPE_MD_TOAP_BYP_CRSN0 | | ||||
| +	      MTK_PPE_MD_TOAP_BYP_CRSN1 | | ||||
| +	      MTK_PPE_MD_TOAP_BYP_CRSN2 | | ||||
|  	      MTK_PPE_FLOW_CFG_IP6_3T_ROUTE | | ||||
|  	      MTK_PPE_FLOW_CFG_IP6_5T_ROUTE | | ||||
|  	      MTK_PPE_FLOW_CFG_IP6_6RD | | ||||
|  	      MTK_PPE_FLOW_CFG_IP4_NAT | | ||||
|  	      MTK_PPE_FLOW_CFG_IP4_NAPT | | ||||
|  	      MTK_PPE_FLOW_CFG_IP4_DSLITE | | ||||
| -	      MTK_PPE_FLOW_CFG_IP4_NAT_FRAG; | ||||
| +	      MTK_PPE_FLOW_CFG_IP4_NAT_FRAG | | ||||
| +	      MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY; | ||||
|  	ppe_w32(ppe, MTK_PPE_FLOW_CFG, val); | ||||
|   | ||||
|  	val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) | | ||||
| @@ -806,6 +822,11 @@ int mtk_ppe_start(struct mtk_ppe *ppe) | ||||
|   | ||||
|  	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0); | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777); | ||||
| +	ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f); | ||||
| +#endif | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| index 1f5cf1c..7012351 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -8,7 +8,11 @@ | ||||
|  #include <linux/bitfield.h> | ||||
|  #include <linux/rhashtable.h> | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#define MTK_ETH_PPE_BASE		0x2000 | ||||
| +#else | ||||
|  #define MTK_ETH_PPE_BASE		0xc00 | ||||
| +#endif | ||||
|   | ||||
|  #define MTK_PPE_ENTRIES_SHIFT		3 | ||||
|  #define MTK_PPE_ENTRIES			(1024 << MTK_PPE_ENTRIES_SHIFT) | ||||
| @@ -16,6 +20,24 @@ | ||||
|  #define MTK_PPE_WAIT_TIMEOUT_US		1000000 | ||||
|   | ||||
|  #define MTK_FOE_IB1_UNBIND_TIMESTAMP	GENMASK(7, 0) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#define MTK_FOE_IB1_UNBIND_SRC_PORT	GENMASK(11, 8) | ||||
| +#define MTK_FOE_IB1_UNBIND_PACKETS	GENMASK(19, 12) | ||||
| +#define MTK_FOE_IB1_UNBIND_PREBIND	BIT(22) | ||||
| +#define MTK_FOE_IB1_UNBIND_PACKET_TYPE	GENMASK(27, 23) | ||||
| +#define MTK_FOE_IB1_BIND_TIMESTAMP	GENMASK(7, 0) | ||||
| +#define MTK_FOE_IB1_BIND_SRC_PORT	GENMASK(11, 8) | ||||
| +#define MTK_FOE_IB1_BIND_MC		BIT(12) | ||||
| +#define MTK_FOE_IB1_BIND_KEEPALIVE	BIT(13) | ||||
| +#define MTK_FOE_IB1_BIND_VLAN_LAYER	GENMASK(16, 14) | ||||
| +#define MTK_FOE_IB1_BIND_PPPOE		BIT(17) | ||||
| +#define MTK_FOE_IB1_BIND_VLAN_TAG	BIT(18) | ||||
| +#define MTK_FOE_IB1_BIND_PKT_SAMPLE	BIT(19) | ||||
| +#define MTK_FOE_IB1_BIND_CACHE		BIT(20) | ||||
| +#define MTK_FOE_IB1_BIND_TUNNEL_DECAP	BIT(21) | ||||
| +#define MTK_FOE_IB1_BIND_TTL		BIT(22) | ||||
| +#define MTK_FOE_IB1_PACKET_TYPE		GENMASK(27, 23) | ||||
| +#else | ||||
|  #define MTK_FOE_IB1_UNBIND_PACKETS	GENMASK(23, 8) | ||||
|  #define MTK_FOE_IB1_UNBIND_PREBIND	BIT(24) | ||||
|   | ||||
| @@ -30,6 +52,8 @@ | ||||
|  #define MTK_FOE_IB1_BIND_TTL		BIT(24) | ||||
|   | ||||
|  #define MTK_FOE_IB1_PACKET_TYPE		GENMASK(27, 25) | ||||
| +#endif | ||||
| + | ||||
|  #define MTK_FOE_IB1_STATE		GENMASK(29, 28) | ||||
|  #define MTK_FOE_IB1_UDP			BIT(30) | ||||
|  #define MTK_FOE_IB1_STATIC		BIT(31) | ||||
| @@ -44,24 +68,42 @@ enum { | ||||
|  	MTK_PPE_PKT_TYPE_IPV6_6RD = 7, | ||||
|  }; | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#define MTK_FOE_IB2_QID			GENMASK(6, 0) | ||||
| +#define MTK_FOE_IB2_PORT_MG		BIT(7) | ||||
| +#define MTK_FOE_IB2_PSE_QOS		BIT(8) | ||||
| +#define MTK_FOE_IB2_DEST_PORT		GENMASK(12, 9) | ||||
| +#define MTK_FOE_IB2_MULTICAST		BIT(13) | ||||
| +#define MTK_FOE_IB2_MIB_CNT		BIT(15) | ||||
| +#define MTK_FOE_IB2_RX_IDX		GENMASK(18, 17) | ||||
| +#define MTK_FOE_IB2_WDMA_WINFO		BIT(19) | ||||
| +#define MTK_FOE_IB2_PORT_AG		GENMASK(23, 20) | ||||
| +#else | ||||
|  #define MTK_FOE_IB2_QID			GENMASK(3, 0) | ||||
|  #define MTK_FOE_IB2_PSE_QOS		BIT(4) | ||||
|  #define MTK_FOE_IB2_DEST_PORT		GENMASK(7, 5) | ||||
|  #define MTK_FOE_IB2_MULTICAST		BIT(8) | ||||
|   | ||||
|  #define MTK_FOE_IB2_WDMA_QID2		GENMASK(13, 12) | ||||
| +#define MTK_FOE_IB2_MIB_CNT		BIT(15) | ||||
|  #define MTK_FOE_IB2_WDMA_DEVIDX		BIT(16) | ||||
|  #define MTK_FOE_IB2_WDMA_WINFO		BIT(17) | ||||
|   | ||||
|  #define MTK_FOE_IB2_PORT_MG		GENMASK(17, 12) | ||||
|   | ||||
|  #define MTK_FOE_IB2_PORT_AG		GENMASK(23, 18) | ||||
| +#endif | ||||
|   | ||||
|  #define MTK_FOE_IB2_DSCP		GENMASK(31, 24) | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#define MTK_FOE_WINFO_BSS		GENMASK(5, 0) | ||||
| +#define MTK_FOE_WINFO_WCID		GENMASK(15, 6) | ||||
| +#else | ||||
|  #define MTK_FOE_VLAN2_WINFO_BSS		GENMASK(5, 0) | ||||
|  #define MTK_FOE_VLAN2_WINFO_WCID	GENMASK(13, 6) | ||||
|  #define MTK_FOE_VLAN2_WINFO_RING	GENMASK(15, 14) | ||||
| +#endif | ||||
|   | ||||
|  enum { | ||||
|  	MTK_FOE_STATE_INVALID, | ||||
| @@ -83,6 +125,11 @@ struct mtk_foe_mac_info { | ||||
|   | ||||
|  	u16 pppoe_id; | ||||
|  	u16 src_mac_lo; | ||||
| + | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	u16 minfo; | ||||
| +	u16 winfo; | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  /* software-only entry type */ | ||||
| @@ -200,7 +247,11 @@ struct mtk_foe_entry { | ||||
|  		struct mtk_foe_ipv4_dslite dslite; | ||||
|  		struct mtk_foe_ipv6 ipv6; | ||||
|  		struct mtk_foe_ipv6_6rd ipv6_6rd; | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +		u32 data[23]; | ||||
| +#else | ||||
|  		u32 data[19]; | ||||
| +#endif | ||||
|  	}; | ||||
|  }; | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| index 8a28572..77594f3 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -193,6 +193,14 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, | ||||
|  		mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss, | ||||
|  				       info.wcid); | ||||
|  		pse_port = PSE_PPE0_PORT; | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +		if (info.wdma_idx == 0) | ||||
| +			pse_port = PSE_WDMA0_PORT; | ||||
| +		else if (info.wdma_idx == 1) | ||||
| +			pse_port = PSE_WDMA1_PORT; | ||||
| +		else | ||||
| +			return -EOPNOTSUPP; | ||||
| +#endif | ||||
|  		*wed_index = info.wdma_idx; | ||||
|  		goto out; | ||||
|  	} | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| index 0c45ea0..d319f18 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| @@ -21,6 +21,9 @@ | ||||
|  #define MTK_PPE_GLO_CFG_BUSY			BIT(31) | ||||
|   | ||||
|  #define MTK_PPE_FLOW_CFG			0x204 | ||||
| +#define MTK_PPE_MD_TOAP_BYP_CRSN0		BIT(1) | ||||
| +#define MTK_PPE_MD_TOAP_BYP_CRSN1		BIT(2) | ||||
| +#define MTK_PPE_MD_TOAP_BYP_CRSN2		BIT(3) | ||||
|  #define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG		BIT(6) | ||||
|  #define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG		BIT(7) | ||||
|  #define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE		BIT(8) | ||||
| @@ -35,6 +38,8 @@ | ||||
|  #define MTK_PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL	BIT(18) | ||||
|  #define MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY	BIT(19) | ||||
|  #define MTK_PPE_FLOW_CFG_IP6_HASH_GRE_KEY	BIT(20) | ||||
| +#define MTK_PPE_FLOW_CFG_IPV4_MAPE_EN		BIT(21) | ||||
| +#define MTK_PPE_FLOW_CFG_IPV4_MAPT_EN		BIT(22) | ||||
|   | ||||
|  #define MTK_PPE_IP_PROTO_CHK			0x208 | ||||
|  #define MTK_PPE_IP_PROTO_CHK_IPV4		GENMASK(15, 0) | ||||
| @@ -54,6 +59,7 @@ | ||||
|  #define MTK_PPE_TB_CFG_HASH_MODE		GENMASK(15, 14) | ||||
|  #define MTK_PPE_TB_CFG_SCAN_MODE		GENMASK(17, 16) | ||||
|  #define MTK_PPE_TB_CFG_HASH_DEBUG		GENMASK(19, 18) | ||||
| +#define MTK_PPE_TB_CFG_INFO_SEL			BIT(20) | ||||
|   | ||||
|  enum { | ||||
|  	MTK_PPE_SCAN_MODE_DISABLED, | ||||
| @@ -111,6 +117,8 @@ enum { | ||||
|   | ||||
|  #define MTK_PPE_DEFAULT_CPU_PORT		0x248 | ||||
|  #define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n)	(GENMASK(2, 0) << ((_n) * 4)) | ||||
| +#define MTK_PPE_DEFAULT_CPU_PORT1		0x24C | ||||
| +#define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n)	(GENMASK(2, 0) << ((_n) * 4)) | ||||
|   | ||||
|  #define MTK_PPE_MTU_DROP			0x308 | ||||
|   | ||||
| @@ -141,4 +149,6 @@ enum { | ||||
|  #define MTK_PPE_MIB_CACHE_CTL_EN		BIT(0) | ||||
|  #define MTK_PPE_MIB_CACHE_CTL_FLUSH		BIT(2) | ||||
|   | ||||
| +#define MTK_PPE_SBW_CTRL			0x374 | ||||
| + | ||||
|  #endif | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,463 +0,0 @@ | ||||
| From 01556d88ad11f0d096d2816b2a69999994e1740f Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Mar 2024 16:26:28 +0800 | ||||
| Subject: [PATCH 05/24] flow-offload-add-mkhnat-dual-ppe-new-v2 | ||||
|  | ||||
| --- | ||||
|  arch/arm64/boot/dts/mediatek/mt7986a.dtsi     |  1 + | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c   | 50 ++++++++++++++----- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h   | 14 ++++-- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c       |  5 +- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.h       |  7 ++- | ||||
|  .../net/ethernet/mediatek/mtk_ppe_debugfs.c   | 27 +++++++--- | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   | 48 ++++++++++++++---- | ||||
|  include/linux/netdevice.h                     |  4 ++ | ||||
|  8 files changed, 119 insertions(+), 37 deletions(-) | ||||
|  mode change 100644 => 100755 drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
|  | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi | ||||
| index 3a4f279..d70151b 100644 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi | ||||
| @@ -483,6 +483,7 @@ | ||||
|                  mediatek,ethsys = <ðsys>; | ||||
|  		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; | ||||
|  		mediatek,wed = <&wed0>, <&wed1>; | ||||
| +                mtketh-ppe-num = <2>; | ||||
|                  #reset-cells = <1>; | ||||
|                  #address-cells = <1>; | ||||
|                  #size-cells = <0>; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index 2fb67e0..7eeddb3 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -2464,7 +2464,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, | ||||
|  #endif | ||||
|   | ||||
|  		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) | ||||
| -			mtk_ppe_check_skb(eth->ppe, skb, hash); | ||||
| +			mtk_ppe_check_skb(eth->ppe[0], skb, hash); | ||||
|   | ||||
|  		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { | ||||
|  			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { | ||||
| @@ -4112,8 +4112,12 @@ static int mtk_open(struct net_device *dev) | ||||
|  		regmap_write(eth->sgmii->pcs[id].regmap, | ||||
|  			     SGMSYS_QPHY_PWR_STATE_CTRL, 0); | ||||
|   | ||||
| -	if (eth->soc->offload_version && mtk_ppe_start(ð->ppe) == 0) | ||||
| -			gdm_config = MTK_GDMA_TO_PPE; | ||||
| +	if (eth->soc->offload_version) { | ||||
| +			gdm_config = MTK_GDMA_TO_PPE0; | ||||
| + | ||||
| +			for (i = 0; i < eth->ppe_num; i++) | ||||
| +				mtk_ppe_start(eth->ppe[i]); | ||||
| +		} | ||||
|   | ||||
|  	mtk_gdm_config(eth, mac->id, gdm_config); | ||||
|   | ||||
| @@ -4202,8 +4206,10 @@ static int mtk_stop(struct net_device *dev) | ||||
|   | ||||
|  	mtk_dma_free(eth); | ||||
|   | ||||
| -	if (eth->soc->offload_version) | ||||
| -		mtk_ppe_stop(eth->ppe); | ||||
| +	if (eth->soc->offload_version) { | ||||
| +		for (i = 0; i < eth->ppe_num; i++) | ||||
| +			mtk_ppe_stop(eth->ppe[i]); | ||||
| +	} | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -5762,15 +5768,35 @@ static int mtk_probe(struct platform_device *pdev) | ||||
|  	} | ||||
|   | ||||
|  	if (eth->soc->offload_version) { | ||||
| -		eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2); | ||||
| -		if (!eth->ppe) { | ||||
| -			err = -ENOMEM; | ||||
| -			goto err_free_dev; | ||||
| +		unsigned int val; | ||||
| +  | ||||
| +		err = of_property_read_u32_index(pdev->dev.of_node, "mtketh-ppe-num", 0, &val); | ||||
| +		if (err < 0) | ||||
| +			eth->ppe_num = 1; | ||||
| +		else | ||||
| +			eth->ppe_num = val; | ||||
| +  | ||||
| +		if (eth->ppe_num > MTK_MAX_PPE_NUM) { | ||||
| +			dev_warn(&pdev->dev, "%d is not a valid ppe num, please check mtketh-ppe-num in dts !", eth->ppe_num); | ||||
| +			eth->ppe_num = MTK_MAX_PPE_NUM; | ||||
|  		} | ||||
|   | ||||
| -		err = mtk_eth_offload_init(eth); | ||||
| -		if (err) | ||||
| -			goto err_free_dev; | ||||
| +		dev_info(&pdev->dev, "ppe num = %d\n", eth->ppe_num); | ||||
| + | ||||
| +		for (i = 0; i < eth->ppe_num; i++) { | ||||
| +			eth->ppe[i] = mtk_ppe_init(eth, | ||||
| +					   eth->base + MTK_ETH_PPE_BASE + i * 0x400, 2, i); | ||||
| +			if (!eth->ppe[i]) { | ||||
| +				err = -ENOMEM; | ||||
| +				goto err_free_dev; | ||||
| +			} | ||||
| + | ||||
| +			err = mtk_eth_offload_init(eth, i); | ||||
| +			if (err) | ||||
| +				goto err_free_dev; | ||||
| +		} | ||||
| + | ||||
| +		mtk_ppe_debugfs_init(eth); | ||||
|  	} | ||||
|   | ||||
|  	for (i = 0; i < MTK_MAX_DEVS; i++) { | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index 910baaf..3995608 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -137,7 +137,12 @@ | ||||
|  #define MTK_GDMA_UCS_EN		BIT(20) | ||||
|  #define MTK_GDMA_STRP_CRC	BIT(16) | ||||
|  #define MTK_GDMA_TO_PDMA	0x0 | ||||
| -#define MTK_GDMA_TO_PPE		0x3333 | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#define MTK_GDMA_TO_PPE0	0x3333 | ||||
| +#define MTK_GDMA_TO_PPE1	0x4444 | ||||
| +#else | ||||
| +#define MTK_GDMA_TO_PPE0	0x4444 | ||||
| +#endif | ||||
|  #define MTK_GDMA_DROP_ALL	0x7777 | ||||
|   | ||||
|  /* GDM Egress Control Register */ | ||||
| @@ -1936,7 +1941,8 @@ struct mtk_eth { | ||||
|  	spinlock_t			syscfg0_lock; | ||||
|  	struct timer_list		mtk_dma_monitor_timer; | ||||
|   | ||||
| -	struct mtk_ppe			*ppe; | ||||
| +	u8				ppe_num; | ||||
| +	struct mtk_ppe			*ppe[MTK_MAX_PPE_NUM]; | ||||
|  	struct rhashtable		flow_table; | ||||
|  }; | ||||
|   | ||||
| @@ -2019,9 +2025,11 @@ int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r); | ||||
|  int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range); | ||||
|  void mtk_usxgmii_link_poll(struct work_struct *work); | ||||
|   | ||||
| -int mtk_eth_offload_init(struct mtk_eth *eth); | ||||
| +int mtk_eth_offload_init(struct mtk_eth *eth, int id); | ||||
|  int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, | ||||
|  		     void *type_data); | ||||
|  void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); | ||||
|  u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index); | ||||
| + | ||||
| +int mtk_ppe_debugfs_init(struct mtk_eth *eth); | ||||
|  #endif /* MTK_ETH_H */ | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index e195fb3..c9ee505 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -696,7 +696,7 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  } | ||||
|   | ||||
|  struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, | ||||
| -		 int version) | ||||
| +		 int version, int id) | ||||
|  { | ||||
|  	struct device *dev = eth->dev; | ||||
|  	struct mtk_foe_entry *foe; | ||||
| @@ -715,6 +715,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, | ||||
|  	ppe->eth = eth; | ||||
|  	ppe->dev = dev; | ||||
|  	ppe->version = version; | ||||
| +	ppe->id = id; | ||||
|   | ||||
|  	foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe), | ||||
|  				  &ppe->foe_phys, GFP_KERNEL); | ||||
| @@ -723,8 +724,6 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, | ||||
|   | ||||
|  	ppe->foe_table = foe; | ||||
|   | ||||
| -	mtk_ppe_debugfs_init(ppe); | ||||
| - | ||||
|  	return ppe; | ||||
|  } | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| index 7012351..86bbac8 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -9,8 +9,10 @@ | ||||
|  #include <linux/rhashtable.h> | ||||
|   | ||||
|  #if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#define MTK_MAX_PPE_NUM			2 | ||||
|  #define MTK_ETH_PPE_BASE		0x2000 | ||||
|  #else | ||||
| +#define MTK_MAX_PPE_NUM			1 | ||||
|  #define MTK_ETH_PPE_BASE		0xc00 | ||||
|  #endif | ||||
|   | ||||
| @@ -299,6 +301,7 @@ struct mtk_flow_entry { | ||||
|  		}; | ||||
|  	}; | ||||
|  	u8 type; | ||||
| +	s8 ppe_index; | ||||
|  	s8 wed_index; | ||||
|  	u16 hash; | ||||
|  	union { | ||||
| @@ -318,6 +321,7 @@ struct mtk_ppe { | ||||
|  	struct device *dev; | ||||
|  	void __iomem *base; | ||||
|  	int version; | ||||
| +	int id; | ||||
|   | ||||
|  	struct mtk_foe_entry *foe_table; | ||||
|  	dma_addr_t foe_phys; | ||||
| @@ -330,7 +334,7 @@ struct mtk_ppe { | ||||
|  	void *acct_table; | ||||
|  }; | ||||
|   | ||||
| -struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version); | ||||
| +struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int id); | ||||
|  int mtk_ppe_start(struct mtk_ppe *ppe); | ||||
|  int mtk_ppe_stop(struct mtk_ppe *ppe); | ||||
|   | ||||
| @@ -381,6 +385,5 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
| -int mtk_ppe_debugfs_init(struct mtk_ppe *ppe); | ||||
|   | ||||
|  #endif | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| index a591ab1..f4ebe59 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| @@ -73,9 +73,8 @@ mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai) | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| -mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind) | ||||
| +mtk_ppe_debugfs_foe_show(struct seq_file *m, struct mtk_ppe *ppe, bool bind) | ||||
|  { | ||||
| -	struct mtk_ppe *ppe = m->private; | ||||
|  	int i; | ||||
|   | ||||
|  	for (i = 0; i < MTK_PPE_ENTRIES; i++) { | ||||
| @@ -122,6 +121,8 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind) | ||||
|  			break; | ||||
|  		} | ||||
|   | ||||
| +		seq_printf(m, " ppe=%d", ppe->id); | ||||
| + | ||||
|  		seq_printf(m, " orig="); | ||||
|  		mtk_print_addr_info(m, &ai); | ||||
|   | ||||
| @@ -164,13 +165,25 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind) | ||||
|  static int | ||||
|  mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private) | ||||
|  { | ||||
| -	return mtk_ppe_debugfs_foe_show(m, private, false); | ||||
| +	struct mtk_eth *eth = m->private; | ||||
| +	int i; | ||||
| + | ||||
| +	for (i = 0; i < eth->ppe_num; i++) | ||||
| +		mtk_ppe_debugfs_foe_show(m, eth->ppe[i], false); | ||||
| + | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
|  static int | ||||
|  mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private) | ||||
|  { | ||||
| -	return mtk_ppe_debugfs_foe_show(m, private, true); | ||||
| +	struct mtk_eth *eth = m->private; | ||||
| +	int i; | ||||
| + | ||||
| +	for (i = 0; i < eth->ppe_num; i++) | ||||
| +		mtk_ppe_debugfs_foe_show(m, eth->ppe[i], true); | ||||
| + | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| @@ -187,7 +200,7 @@ mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file) | ||||
|  			   inode->i_private); | ||||
|  } | ||||
|   | ||||
| -int mtk_ppe_debugfs_init(struct mtk_ppe *ppe) | ||||
| +int mtk_ppe_debugfs_init(struct mtk_eth *eth) | ||||
|  { | ||||
|  	static const struct file_operations fops_all = { | ||||
|  		.open = mtk_ppe_debugfs_foe_open_all, | ||||
| @@ -209,8 +222,8 @@ int mtk_ppe_debugfs_init(struct mtk_ppe *ppe) | ||||
|  	if (!root) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| -	debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all); | ||||
| -	debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind); | ||||
| +	debugfs_create_file("entries", S_IRUGO, root, eth, &fops_all); | ||||
| +	debugfs_create_file("bind", S_IRUGO, root, eth, &fops_bind); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| old mode 100644 | ||||
| new mode 100755 | ||||
| index 77594f3..f256607 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -229,9 +229,12 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	struct flow_action_entry *act; | ||||
|  	struct mtk_flow_data data = {}; | ||||
|  	struct mtk_foe_entry foe; | ||||
| -	struct net_device *odev = NULL; | ||||
| +	struct net_device *idev = NULL, *odev = NULL; | ||||
|  	struct mtk_flow_entry *entry; | ||||
| +	struct net_device_path_ctx ctx = {}; | ||||
| +	struct net_device_path path = {}; | ||||
|  	int offload_type = 0; | ||||
| +	int ppe_index = 0; | ||||
|  	int wed_index = -1; | ||||
|  	u16 addr_type = 0; | ||||
|  	u8 l4proto = 0; | ||||
| @@ -245,6 +248,10 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  		struct flow_match_meta match; | ||||
|   | ||||
|  		flow_rule_match_meta(rule, &match); | ||||
| +		idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex); | ||||
| + | ||||
| +		if (!idev) | ||||
| +			pr_info("[%s] idev doesn't exist !\n", __func__); | ||||
|  	} else { | ||||
|  		return -EOPNOTSUPP; | ||||
|  	} | ||||
| @@ -347,6 +354,20 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	if (idev && idev->netdev_ops->ndo_fill_receive_path) { | ||||
| +		ctx.dev = idev; | ||||
| +		idev->netdev_ops->ndo_fill_receive_path(&ctx, &path); | ||||
| +		ppe_index = path.mtk_wdma.wdma_idx; | ||||
| +		if (ppe_index >= eth->ppe_num) { | ||||
| +			if (printk_ratelimit()) | ||||
| +				pr_info("[%s] PPE%d doesn't exist, please check mtketh-ppe-num in dts !\n", __func__, ppe_index); | ||||
| + | ||||
| +			return -EINVAL; | ||||
| +		} | ||||
| +	} | ||||
| +#endif | ||||
| + | ||||
|  	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { | ||||
|  		struct flow_match_ports ports; | ||||
|   | ||||
| @@ -440,9 +461,10 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|   | ||||
|  	entry->cookie = f->cookie; | ||||
|  	memcpy(&entry->data, &foe, sizeof(entry->data)); | ||||
| +	entry->ppe_index = ppe_index; | ||||
|  	entry->wed_index = wed_index; | ||||
|   | ||||
| -	if (mtk_foe_entry_commit(eth->ppe, entry) < 0) | ||||
| +	if (mtk_foe_entry_commit(eth->ppe[ppe_index], entry) < 0) | ||||
|  		goto free; | ||||
|   | ||||
|  	err = rhashtable_insert_fast(ð->flow_table, &entry->node, | ||||
| @@ -453,7 +475,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	return 0; | ||||
|   | ||||
|  clear: | ||||
| -	mtk_foe_entry_clear(eth->ppe, entry); | ||||
| +	mtk_foe_entry_clear(eth->ppe[ppe_index], entry); | ||||
|  free: | ||||
|  	kfree(entry); | ||||
|  	if (wed_index >= 0) | ||||
| @@ -465,13 +487,15 @@ static int | ||||
|  mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  { | ||||
|  	struct mtk_flow_entry *entry; | ||||
| +	int i; | ||||
|   | ||||
|  	entry = rhashtable_lookup(ð->flow_table, &f->cookie, | ||||
|  				  mtk_flow_ht_params); | ||||
|  	if (!entry) | ||||
|  		return -ENOENT; | ||||
|   | ||||
| -	mtk_foe_entry_clear(eth->ppe, entry); | ||||
| +	i = entry->ppe_index; | ||||
| +	mtk_foe_entry_clear(eth->ppe[i], entry); | ||||
|  	rhashtable_remove_fast(ð->flow_table, &entry->node, | ||||
|  			       mtk_flow_ht_params); | ||||
|  	if (entry->wed_index >= 0) | ||||
| @@ -486,13 +510,15 @@ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  { | ||||
|  	struct mtk_flow_entry *entry; | ||||
|  	u32 idle; | ||||
| +	int i; | ||||
|   | ||||
|  	entry = rhashtable_lookup(ð->flow_table, &f->cookie, | ||||
|  				  mtk_flow_ht_params); | ||||
|  	if (!entry) | ||||
|  		return -ENOENT; | ||||
|   | ||||
| -	idle = mtk_foe_entry_idle_time(eth->ppe, entry); | ||||
| +	i = entry->ppe_index; | ||||
| +	idle = mtk_foe_entry_idle_time(eth->ppe[i], entry); | ||||
|  	f->stats.lastused = jiffies - idle * HZ; | ||||
|   | ||||
|  	return 0; | ||||
| @@ -543,10 +569,12 @@ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f) | ||||
|  	static LIST_HEAD(block_cb_list); | ||||
|  	struct flow_block_cb *block_cb; | ||||
|  	flow_setup_cb_t *cb; | ||||
| -	int err = 0; | ||||
| +	int i, err = 0; | ||||
|   | ||||
| -	if (!eth->ppe || !eth->ppe->foe_table) | ||||
| -		return -EOPNOTSUPP; | ||||
| +	for (i = 0; i < eth->ppe_num; i++) { | ||||
| +		if (!eth->ppe[i] || !eth->ppe[i]->foe_table) | ||||
| +			return -EOPNOTSUPP; | ||||
| +	} | ||||
|   | ||||
|  	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) | ||||
|  		return -EOPNOTSUPP; | ||||
| @@ -603,9 +631,9 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -int mtk_eth_offload_init(struct mtk_eth *eth) | ||||
| +int mtk_eth_offload_init(struct mtk_eth *eth, int id) | ||||
|  { | ||||
| -	if (!eth->ppe || !eth->ppe->foe_table) | ||||
| +	if (!eth->ppe[id] || !eth->ppe[id]->foe_table) | ||||
|  		return 0; | ||||
|   | ||||
|  	return rhashtable_init(ð->flow_table, &mtk_flow_ht_params); | ||||
| diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h | ||||
| index 5305384..b2abebe 100644 | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -1316,6 +1316,8 @@ struct tlsdev_ops; | ||||
|   *	rtnl_lock is not held. | ||||
|   * int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path); | ||||
|   *     Get the forwarding path to reach the real device from the HW destination address | ||||
| + * int (*ndo_fill_receive_path)(struct net_device_path_ctx *ctx, struct net_device_path *path); | ||||
| + *     Get the receiving path to reach the real device from the HW source address | ||||
|   */ | ||||
|  struct net_device_ops { | ||||
|  	int			(*ndo_init)(struct net_device *dev); | ||||
| @@ -1515,6 +1517,8 @@ struct net_device_ops { | ||||
|  	struct devlink_port *	(*ndo_get_devlink_port)(struct net_device *dev); | ||||
|  	int                     (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, | ||||
|                                                           struct net_device_path *path); | ||||
| +	int                     (*ndo_fill_receive_path)(struct net_device_path_ctx *ctx, | ||||
| +							 struct net_device_path *path); | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,73 +0,0 @@ | ||||
| From 7c68ae1b991064bc0904313c56b83d2f3e03ccd7 Mon Sep 17 00:00:00 2001 | ||||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 11:03:33 +0800 | ||||
| Subject: [PATCH 07/24] add-wed-tx-wds-support-for-netsys2 | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed.c      | 6 ++++-- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed_regs.h | 1 + | ||||
|  include/linux/soc/mediatek/mtk_wed.h         | 3 +++ | ||||
|  3 files changed, 8 insertions(+), 2 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| index 02e06a8..ea8b2db 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| @@ -813,7 +813,7 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) | ||||
|  		val |= BIT(0) | (BIT(1) * !!dev->hw->index); | ||||
|  		regmap_write(dev->hw->mirror, dev->hw->index * 4, val); | ||||
|  	} else { | ||||
| -		mtk_wed_set_512_support(dev, true); | ||||
| +		mtk_wed_set_512_support(dev, dev->wlan.wcid_512); | ||||
|  	} | ||||
|   | ||||
|  	mtk_wed_dma_enable(dev); | ||||
| @@ -869,9 +869,11 @@ mtk_wed_attach(struct mtk_wed_device *dev) | ||||
|   | ||||
|  	mtk_wed_hw_init_early(dev); | ||||
|   | ||||
| -	if (hw->hifsys) | ||||
| +	if (hw->version == 1) | ||||
|  		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, | ||||
|  				   BIT(hw->index), 0); | ||||
| +	else | ||||
| +		dev->rev_id = wed_r32(dev, MTK_WED_REV_ID); | ||||
|   | ||||
|  out: | ||||
|  	mutex_unlock(&hw_lock); | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| index e66acda..e797e9d 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| @@ -26,6 +26,7 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_REV_ID					0x000 | ||||
|  #define MTK_WED_REV_ID_MAJOR				GENMASK(7, 0) | ||||
|  #endif | ||||
| +#define MTK_WED_REV_ID_MINOR				GENMASK(27, 16) | ||||
|   | ||||
|  #define MTK_WED_RESET					0x008 | ||||
|  #define MTK_WED_RESET_TX_BM				BIT(0) | ||||
| diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h | ||||
| index 4db70b0..00036f9 100644 | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h | ||||
| @@ -35,6 +35,7 @@ struct mtk_wed_device { | ||||
|  	bool init_done, running; | ||||
|  	int wdma_idx; | ||||
|  	int irq; | ||||
| +	u32 rev_id; | ||||
|   | ||||
|  	struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES]; | ||||
|  	struct mtk_wed_ring txfree_ring; | ||||
| @@ -68,6 +69,8 @@ struct mtk_wed_device { | ||||
|  		u16 token_start; | ||||
|  		unsigned int nbuf; | ||||
|   | ||||
| +		bool wcid_512; | ||||
| + | ||||
|  		u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); | ||||
|  		int (*offload_enable)(struct mtk_wed_device *wed); | ||||
|  		void (*offload_disable)(struct mtk_wed_device *wed); | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,860 +0,0 @@ | ||||
| From 7f1319357888271ea4aeeda81723b19a8f5ef2c0 Mon Sep 17 00:00:00 2001 | ||||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 11:05:45 +0800 | ||||
| Subject: [PATCH] add-wed-ser-support | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c  |   8 + | ||||
|  drivers/net/ethernet/mediatek/mtk_wed.c      | 391 ++++++++++++++----- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed.h      |  10 + | ||||
|  drivers/net/ethernet/mediatek/mtk_wed_regs.h |   9 + | ||||
|  include/linux/soc/mediatek/mtk_wed.h         |  25 +- | ||||
|  5 files changed, 342 insertions(+), 101 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index 268c9e7..a24b223 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -4619,6 +4619,9 @@ static void mtk_pending_work(struct work_struct *work) | ||||
|  	for (i = 0; i < MTK_MAC_COUNT; i++) { | ||||
|  		if (!eth->netdev[i]) | ||||
|  			continue; | ||||
| +#ifdef CONFIG_NET_MEDIATEK_SOC_WED | ||||
| +		mtk_wed_fe_reset(); | ||||
| +#else | ||||
|  		if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) { | ||||
|  			pr_info("send MTK_FE_STOP_TRAFFIC event\n"); | ||||
|  			call_netdevice_notifiers(MTK_FE_STOP_TRAFFIC, | ||||
| @@ -4644,6 +4647,7 @@ static void mtk_pending_work(struct work_struct *work) | ||||
|  			pr_warn("wait for MTK_FE_START_RESET\n"); | ||||
|  		} | ||||
|  		rtnl_lock(); | ||||
| +#endif | ||||
|  		break; | ||||
|  	} | ||||
|   | ||||
| @@ -4682,6 +4686,9 @@ static void mtk_pending_work(struct work_struct *work) | ||||
|  	for (i = 0; i < MTK_MAC_COUNT; i++) { | ||||
|  		if (!eth->netdev[i]) | ||||
|  			continue; | ||||
| +#ifdef CONFIG_NET_MEDIATEK_SOC_WED | ||||
| +		mtk_wed_fe_reset_complete(); | ||||
| +#else | ||||
|  		if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) { | ||||
|  			pr_info("send MTK_FE_START_TRAFFIC event\n"); | ||||
|  			call_netdevice_notifiers(MTK_FE_START_TRAFFIC, | ||||
| @@ -4691,6 +4698,7 @@ static void mtk_pending_work(struct work_struct *work) | ||||
|  			call_netdevice_notifiers(MTK_FE_RESET_DONE, | ||||
|  				eth->netdev[i]); | ||||
|  		} | ||||
| +#endif | ||||
|  		call_netdevice_notifiers(MTK_FE_RESET_NAT_DONE, | ||||
|  			eth->netdev[i]); | ||||
|  		break; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| index ad9f3d5..b993f0e 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| @@ -13,8 +13,10 @@ | ||||
|  #include <linux/debugfs.h> | ||||
|  #include <linux/iopoll.h> | ||||
|  #include <linux/soc/mediatek/mtk_wed.h> | ||||
| +#include <net/rtnetlink.h> | ||||
|   | ||||
|  #include "mtk_eth_soc.h" | ||||
| +#include "mtk_eth_reset.h" | ||||
|  #include "mtk_wed_regs.h" | ||||
|  #include "mtk_wed.h" | ||||
|  #include "mtk_ppe.h" | ||||
| @@ -80,10 +82,13 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev) | ||||
|   | ||||
|  	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN); | ||||
|  	ret = readx_poll_timeout(mtk_wdma_read_reset, dev, status, | ||||
| -				 !(status & mask), 0, 1000) | ||||
| +				 !(status & mask), 0, 10000); | ||||
|  	if (ret) | ||||
|  		dev_err(dev->hw->dev, "rx reset failed \n"); | ||||
|   | ||||
| +	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); | ||||
| +	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
| + | ||||
|  	for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) { | ||||
|  		if (!dev->rx_wdma[i].desc) | ||||
|  			continue; | ||||
| @@ -91,6 +96,8 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev) | ||||
|  		wdma_w32(dev, | ||||
|  			 MTK_WDMA_RING_RX(i) + MTK_WED_RING_OFS_CPU_IDX, 0); | ||||
|  	} | ||||
| + | ||||
| +	return ret; | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -101,16 +108,15 @@ mtk_wdma_tx_reset(struct mtk_wed_device *dev) | ||||
|   | ||||
|  	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); | ||||
|  	if (readx_poll_timeout(mtk_wdma_read_reset, dev, status, | ||||
| -			       !(status & mask), 0, 1000)) | ||||
| +			       !(status & mask), 0, 10000)) | ||||
|  		dev_err(dev->hw->dev, "tx reset failed \n"); | ||||
|   | ||||
| -	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) { | ||||
| -		if (!dev->tx_wdma[i].desc) | ||||
| -			continue; | ||||
| +	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); | ||||
| +	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
|   | ||||
| +	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) | ||||
|  		wdma_w32(dev, | ||||
|  			 MTK_WDMA_RING_TX(i) + MTK_WED_RING_OFS_CPU_IDX, 0); | ||||
| -	} | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -176,6 +182,59 @@ mtk_wed_wo_reset(struct mtk_wed_device *dev) | ||||
|  	iounmap((void *)reg); | ||||
|  } | ||||
|   | ||||
| +void mtk_wed_fe_reset(void) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	mutex_lock(&hw_lock); | ||||
| + | ||||
| +	for (i = 0; i < ARRAY_SIZE(hw_list); i++) { | ||||
| +		struct mtk_wed_hw *hw = hw_list[i]; | ||||
| +		struct mtk_wed_device *dev; | ||||
| +		int err; | ||||
| + | ||||
| +		if (!hw) | ||||
| +			break; | ||||
| + | ||||
| +		dev = hw->wed_dev; | ||||
| +		if (!dev || !dev->wlan.reset) | ||||
| +			continue; | ||||
| + | ||||
| +		pr_info("%s: receive fe reset start event, trigger SER\n", __func__); | ||||
| + | ||||
| +		/* reset callback blocks until WLAN reset is completed */ | ||||
| +		err = dev->wlan.reset(dev); | ||||
| +		if (err) | ||||
| +			dev_err(dev->dev, "wlan reset failed: %d\n", err); | ||||
| +	} | ||||
| + | ||||
| +	mutex_unlock(&hw_lock); | ||||
| +} | ||||
| + | ||||
| +void mtk_wed_fe_reset_complete(void) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	mutex_lock(&hw_lock); | ||||
| + | ||||
| +	for (i = 0; i < ARRAY_SIZE(hw_list); i++) { | ||||
| +		struct mtk_wed_hw *hw = hw_list[i]; | ||||
| +		struct mtk_wed_device *dev; | ||||
| + | ||||
| +		if (!hw) | ||||
| +			break; | ||||
| + | ||||
| +		dev = hw->wed_dev; | ||||
| +		if (!dev || !dev->wlan.reset_complete) | ||||
| +			continue; | ||||
| + | ||||
| +		pr_info("%s: receive fe reset done event, continue SER\n", __func__); | ||||
| +		dev->wlan.reset_complete(dev); | ||||
| +	} | ||||
| + | ||||
| +	mutex_unlock(&hw_lock); | ||||
| +} | ||||
| + | ||||
|  static struct mtk_wed_hw * | ||||
|  mtk_wed_assign(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -473,8 +532,8 @@ mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx) | ||||
|  	} | ||||
|   | ||||
|  	if (i == 3) { | ||||
| -		dev_err(dev->hw->dev, "mtk_wed%d: rx dma enable failed!\n", | ||||
| -			       dev->hw->index); | ||||
| +		dev_err(dev->hw->dev, "mtk_wed%d: rx(%d) dma enable failed!\n", | ||||
| +			       dev->hw->index, idx); | ||||
|  		return; | ||||
|  	} | ||||
|   | ||||
| @@ -522,16 +581,8 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev) | ||||
|  static void | ||||
|  mtk_wed_stop(struct mtk_wed_device *dev) | ||||
|  { | ||||
| -	mtk_wed_dma_disable(dev); | ||||
| - | ||||
|  	mtk_wed_set_ext_int(dev, false); | ||||
|   | ||||
| -	wed_clr(dev, MTK_WED_CTRL, | ||||
| -		MTK_WED_CTRL_WDMA_INT_AGENT_EN | | ||||
| -		MTK_WED_CTRL_WPDMA_INT_AGENT_EN | | ||||
| -		MTK_WED_CTRL_WED_TX_BM_EN | | ||||
| -		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
| - | ||||
|  	wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0); | ||||
|  	wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0); | ||||
|  	wdma_w32(dev, MTK_WDMA_INT_MASK, 0); | ||||
| @@ -543,39 +594,49 @@ mtk_wed_stop(struct mtk_wed_device *dev) | ||||
|   | ||||
|  	wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); | ||||
|  	wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0); | ||||
| -	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); | ||||
| - | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| -mtk_wed_detach(struct mtk_wed_device *dev) | ||||
| +mtk_wed_deinit(struct mtk_wed_device *dev) | ||||
|  { | ||||
| -	struct device_node *wlan_node; | ||||
| -	struct mtk_wed_hw *hw = dev->hw; | ||||
| +	mtk_wed_stop(dev); | ||||
| +	mtk_wed_dma_disable(dev); | ||||
|   | ||||
| -	mutex_lock(&hw_lock); | ||||
| +	wed_clr(dev, MTK_WED_CTRL, | ||||
| +		MTK_WED_CTRL_WDMA_INT_AGENT_EN | | ||||
| +		MTK_WED_CTRL_WPDMA_INT_AGENT_EN | | ||||
| +		MTK_WED_CTRL_WED_TX_BM_EN | | ||||
| +		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
|   | ||||
| -	mtk_wed_stop(dev); | ||||
| +	if (dev->hw->version == 1) | ||||
| +		return; | ||||
|   | ||||
| -	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); | ||||
| -	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
| +	wed_clr(dev, MTK_WED_CTRL, | ||||
| +		MTK_WED_CTRL_RX_ROUTE_QM_EN | | ||||
| +		MTK_WED_CTRL_WED_RX_BM_EN | | ||||
| +		MTK_WED_CTRL_RX_RRO_QM_EN); | ||||
| +} | ||||
|   | ||||
| -	mtk_wed_reset(dev, MTK_WED_RESET_WED); | ||||
| +static void | ||||
| +__mtk_wed_detach(struct mtk_wed_device *dev) | ||||
| +{ | ||||
| +	struct device_node *wlan_node; | ||||
| +	struct mtk_wed_hw *hw = dev->hw; | ||||
|   | ||||
| -	if (mtk_wed_get_rx_capa(dev)) { | ||||
| -		wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); | ||||
| -		wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); | ||||
| -		wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
| -	} | ||||
| +	mtk_wed_deinit(dev); | ||||
|   | ||||
| +	mtk_wdma_rx_reset(dev); | ||||
| +	mtk_wed_reset(dev, MTK_WED_RESET_WED); | ||||
|  	mtk_wed_free_tx_buffer(dev); | ||||
|  	mtk_wed_free_tx_rings(dev); | ||||
|   | ||||
|  	if (mtk_wed_get_rx_capa(dev)) { | ||||
| -		mtk_wed_wo_reset(dev); | ||||
| +		if(hw->wed_wo) | ||||
| +			mtk_wed_wo_reset(dev); | ||||
|  		mtk_wed_free_rx_rings(dev); | ||||
| -		mtk_wed_wo_exit(hw); | ||||
| -		mtk_wdma_rx_reset(dev); | ||||
| +		if(hw->wed_wo) | ||||
| +			mtk_wed_wo_exit(hw); | ||||
| +		mtk_wdma_tx_reset(dev); | ||||
|  	} | ||||
|   | ||||
|  	if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { | ||||
| @@ -593,6 +654,13 @@ mtk_wed_detach(struct mtk_wed_device *dev) | ||||
|  	module_put(THIS_MODULE); | ||||
|   | ||||
|  	hw->wed_dev = NULL; | ||||
| +} | ||||
| + | ||||
| +static void | ||||
| +mtk_wed_detach(struct mtk_wed_device *dev) | ||||
| +{ | ||||
| +	mutex_lock(&hw_lock); | ||||
| +	__mtk_wed_detach(dev); | ||||
|  	mutex_unlock(&hw_lock); | ||||
|  } | ||||
|   | ||||
| @@ -665,7 +733,7 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev) | ||||
|  { | ||||
|  	u32 mask, set; | ||||
|   | ||||
| -	mtk_wed_stop(dev); | ||||
| +	mtk_wed_deinit(dev); | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_WED); | ||||
|  	mtk_wed_set_wpdma(dev); | ||||
|   | ||||
| @@ -715,7 +783,6 @@ mtk_wed_rro_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, | ||||
|   | ||||
|  	ring->desc_size = sizeof(*ring->desc); | ||||
|  	ring->size = size; | ||||
| -	memset(ring->desc, 0, size); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -938,44 +1005,140 @@ mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx) | ||||
|  } | ||||
|   | ||||
|  static u32 | ||||
| -mtk_wed_check_busy(struct mtk_wed_device *dev) | ||||
| +mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
|  { | ||||
| -	if (wed_r32(dev, MTK_WED_GLO_CFG) & MTK_WED_GLO_CFG_TX_DMA_BUSY) | ||||
| -		return true; | ||||
| - | ||||
| -	if (wed_r32(dev, MTK_WED_WPDMA_GLO_CFG) & | ||||
| -	    MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY) | ||||
| -		return true; | ||||
| - | ||||
| -	if (wed_r32(dev, MTK_WED_CTRL) & MTK_WED_CTRL_WDMA_INT_AGENT_BUSY) | ||||
| -		return true; | ||||
| - | ||||
| -	if (wed_r32(dev, MTK_WED_WDMA_GLO_CFG) & | ||||
| -	    MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY) | ||||
| -		return true; | ||||
| - | ||||
| -	if (wdma_r32(dev, MTK_WDMA_GLO_CFG) & | ||||
| -	    MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY) | ||||
| -		return true; | ||||
| - | ||||
| -	if (wed_r32(dev, MTK_WED_CTRL) & | ||||
| -	    (MTK_WED_CTRL_WED_TX_BM_BUSY | MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY)) | ||||
| +	if (wed_r32(dev, reg) & mask) | ||||
|  		return true; | ||||
|   | ||||
|  	return false; | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| -mtk_wed_poll_busy(struct mtk_wed_device *dev) | ||||
| +mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
|  { | ||||
| -	int sleep = 15000; | ||||
| +	int sleep = 1000; | ||||
|  	int timeout = 100 * sleep; | ||||
|  	u32 val; | ||||
|   | ||||
|  	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, | ||||
| -				 timeout, false, dev); | ||||
| +				 timeout, false, dev, reg, mask); | ||||
|  } | ||||
|   | ||||
| +static int | ||||
| +mtk_wed_rx_reset(struct mtk_wed_device *dev) | ||||
| +{ | ||||
| +	struct mtk_wed_wo *wo = dev->hw->wed_wo; | ||||
| +	u8 val = WO_STATE_SER_RESET; | ||||
| +	int i, ret; | ||||
| + | ||||
| +	ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, | ||||
| +				   MTK_WED_WO_CMD_CHANGE_STATE, &val, | ||||
| +				   sizeof(val), true); | ||||
| + | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN); | ||||
| +	ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, | ||||
| +				MTK_WED_WPDMA_RX_D_RX_DRV_BUSY); | ||||
| +	if (ret) { | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV); | ||||
| +	} else { | ||||
| +		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, | ||||
| +			MTK_WED_WPDMA_RX_D_RST_CRX_IDX | | ||||
| +			MTK_WED_WPDMA_RX_D_RST_DRV_IDX); | ||||
| + | ||||
| +		wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, | ||||
| +			MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE | | ||||
| +			MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE); | ||||
| +		wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, | ||||
| +			MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE | | ||||
| +			MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE); | ||||
| + | ||||
| +		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0); | ||||
| +	} | ||||
| + | ||||
| +	/* reset rro qm */ | ||||
| +	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_RRO_QM_EN); | ||||
| +	ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
| +				MTK_WED_CTRL_RX_RRO_QM_BUSY); | ||||
| +	if (ret) { | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_RX_RRO_QM); | ||||
| +	} else { | ||||
| +		wed_set(dev, MTK_WED_RROQM_RST_IDX, | ||||
| +			MTK_WED_RROQM_RST_IDX_MIOD | | ||||
| +			MTK_WED_RROQM_RST_IDX_FDBK); | ||||
| +		wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0); | ||||
| +	} | ||||
| + | ||||
| +	/* reset route qm */ | ||||
| +	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); | ||||
| +	ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
| +				MTK_WED_CTRL_RX_ROUTE_QM_BUSY); | ||||
| +	if (ret) { | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM); | ||||
| +	} else { | ||||
| +		wed_set(dev, MTK_WED_RTQM_GLO_CFG, | ||||
| +			MTK_WED_RTQM_Q_RST); | ||||
| +	} | ||||
| + | ||||
| +	/* reset tx wdma */ | ||||
| +	mtk_wdma_tx_reset(dev); | ||||
| + | ||||
| +	/* reset tx wdma drv */ | ||||
| +	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN); | ||||
| +	mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
| +			  MTK_WED_CTRL_WDMA_INT_AGENT_BUSY); | ||||
| +	mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV); | ||||
| + | ||||
| +	/* reset wed rx dma */ | ||||
| +	ret = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG, | ||||
| +				MTK_WED_GLO_CFG_RX_DMA_BUSY); | ||||
| +	wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_RX_DMA_EN); | ||||
| +	if (ret) { | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA); | ||||
| +	} else { | ||||
| +		struct mtk_eth *eth = dev->hw->eth; | ||||
| + | ||||
| +		if(MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) | ||||
| +			wed_set(dev, MTK_WED_RESET_IDX, | ||||
| +				MTK_WED_RESET_IDX_RX_V2); | ||||
| +		else | ||||
| +			wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX); | ||||
| +		wed_w32(dev, MTK_WED_RESET_IDX, 0); | ||||
| +	} | ||||
| + | ||||
| +	/* reset rx bm */ | ||||
| +	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); | ||||
| +	mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
| +			  MTK_WED_CTRL_WED_RX_BM_BUSY); | ||||
| +	mtk_wed_reset(dev, MTK_WED_RESET_RX_BM); | ||||
| + | ||||
| +	/* wo change to enable state */ | ||||
| +	val = WO_STATE_ENABLE; | ||||
| +	ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, | ||||
| +				   MTK_WED_WO_CMD_CHANGE_STATE, &val, | ||||
| +				   sizeof(val), true); | ||||
| + | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* wed_rx_ring_reset */ | ||||
| +	for (i = 0; i < ARRAY_SIZE(dev->rx_ring); i++) { | ||||
| +		if (!dev->rx_ring[i].desc) | ||||
| +			continue; | ||||
| + | ||||
| +		mtk_wed_ring_reset(&dev->rx_ring[i], MTK_WED_RX_RING_SIZE, | ||||
| +				   false); | ||||
| +	} | ||||
| + | ||||
| +	mtk_wed_free_rx_buffer(dev); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| + | ||||
|  static void | ||||
|  mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -991,22 +1154,25 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  				   true); | ||||
|  	} | ||||
|   | ||||
| -	if (mtk_wed_poll_busy(dev)) | ||||
| -		busy = mtk_wed_check_busy(dev); | ||||
| +	/* 1.Reset WED Tx DMA */ | ||||
| +	wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_TX_DMA_EN); | ||||
| +	busy = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG, | ||||
| +				 MTK_WED_GLO_CFG_TX_DMA_BUSY); | ||||
|   | ||||
|  	if (busy) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA); | ||||
|  	} else { | ||||
| -		wed_w32(dev, MTK_WED_RESET_IDX, | ||||
| -			MTK_WED_RESET_IDX_TX | | ||||
| -			MTK_WED_RESET_IDX_RX); | ||||
| +		wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX); | ||||
|  		wed_w32(dev, MTK_WED_RESET_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| -	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); | ||||
| -	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
| +	/* 2. Reset WDMA Rx DMA/Driver_Engine */ | ||||
| +	busy = !!mtk_wdma_rx_reset(dev); | ||||
|   | ||||
| -	mtk_wdma_rx_reset(dev); | ||||
| +	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); | ||||
| +	if (!busy) | ||||
| +		busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG, | ||||
| +					 MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY); | ||||
|   | ||||
|  	if (busy) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); | ||||
| @@ -1023,6 +1189,9 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  			MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE); | ||||
|  	} | ||||
|   | ||||
| +	/* 3. Reset WED WPDMA Tx Driver Engine */ | ||||
| +	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
| + | ||||
|  	for (i = 0; i < 100; i++) { | ||||
|  		val = wed_r32(dev, MTK_WED_TX_BM_INTF); | ||||
|  		if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40) | ||||
| @@ -1030,8 +1199,21 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  	} | ||||
|   | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT); | ||||
| +	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_BM_EN); | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); | ||||
|   | ||||
| +	/* 4. Reset WED WPDMA Tx Driver Engine */ | ||||
| +	busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG, | ||||
| +				 MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY); | ||||
| + | ||||
| +	wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, | ||||
| +		MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | | ||||
| +		MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); | ||||
| + | ||||
| +	if(!busy) | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG, | ||||
| +				  MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY); | ||||
| + | ||||
|  	if (busy) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV); | ||||
| @@ -1043,6 +1225,16 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| +	dev->init_done = false; | ||||
| +	if (dev->hw->version == 1) | ||||
| +		return; | ||||
| + | ||||
| +	if (!busy) { | ||||
| +		wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_WPDMA_IDX_RX); | ||||
| +		wed_w32(dev, MTK_WED_RESET_IDX, 0); | ||||
| +	} | ||||
| + | ||||
| +	mtk_wed_rx_reset(dev); | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| @@ -1062,7 +1254,8 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| -mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) | ||||
| +mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size, | ||||
| +			   bool reset) | ||||
|  { | ||||
|  	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; | ||||
|  	struct mtk_wed_ring *wdma; | ||||
| @@ -1071,8 +1264,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	wdma = &dev->rx_wdma[idx]; | ||||
| -	if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size, | ||||
| -			       true)) | ||||
| +	if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, | ||||
| +					 desc_size, true)) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
|  	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, | ||||
| @@ -1090,7 +1283,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| -mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size) | ||||
| +mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size, | ||||
| +			   bool reset) | ||||
|  { | ||||
|  	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; | ||||
|  	struct mtk_wed_ring *wdma; | ||||
| @@ -1099,8 +1293,8 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size) | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	wdma = &dev->tx_wdma[idx]; | ||||
| -	if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, | ||||
| -			       desc_size, true)) | ||||
| +	if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, | ||||
| +					 desc_size, true)) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
|  	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, | ||||
| @@ -1112,6 +1306,9 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size) | ||||
|  	wdma_w32(dev, | ||||
|  		 MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0); | ||||
|   | ||||
| +	if (reset) | ||||
| +		mtk_wed_ring_reset(wdma, MTK_WED_WDMA_RING_SIZE, true); | ||||
| + | ||||
|  	if (!idx)  { | ||||
|  		wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE, | ||||
|  			wdma->desc_phys); | ||||
| @@ -1267,9 +1464,12 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) | ||||
|  { | ||||
|  	int i; | ||||
|   | ||||
| +	if (mtk_wed_get_rx_capa(dev) && mtk_wed_rx_buffer_alloc(dev)) | ||||
| +		return; | ||||
| + | ||||
|  	for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) | ||||
|  		if (!dev->rx_wdma[i].desc) | ||||
| -			mtk_wed_wdma_rx_ring_setup(dev, i, 16); | ||||
| +			mtk_wed_wdma_rx_ring_setup(dev, i, 16, false); | ||||
|   | ||||
|   | ||||
|  	mtk_wed_hw_init(dev); | ||||
| @@ -1278,10 +1478,9 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) | ||||
|  	mtk_wed_set_ext_int(dev, true); | ||||
|   | ||||
|  	if (dev->hw->version == 1) { | ||||
| -		u32 val; | ||||
| - | ||||
| -		val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN | | ||||
| -		      FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, dev->hw->index); | ||||
| +		u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN | | ||||
| +			  FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, | ||||
| +				     dev->hw->index); | ||||
|   | ||||
|  		val |= BIT(0) | (BIT(1) * !!dev->hw->index); | ||||
|  		regmap_write(dev->hw->mirror, dev->hw->index * 4, val); | ||||
| @@ -1353,10 +1552,6 @@ mtk_wed_attach(struct mtk_wed_device *dev) | ||||
|  		goto out; | ||||
|   | ||||
|  	if (mtk_wed_get_rx_capa(dev)) { | ||||
| -		ret = mtk_wed_rx_buffer_alloc(dev); | ||||
| -		if (ret) | ||||
| -			goto out; | ||||
| - | ||||
|  		ret = mtk_wed_rro_alloc(dev); | ||||
|  		if (ret) | ||||
|  			goto out; | ||||
| @@ -1364,6 +1559,10 @@ mtk_wed_attach(struct mtk_wed_device *dev) | ||||
|   | ||||
|  	mtk_wed_hw_init_early(dev); | ||||
|   | ||||
| +	init_completion(&dev->fe_reset_done); | ||||
| +	init_completion(&dev->wlan_reset_done); | ||||
| +	atomic_set(&dev->fe_reset, 0); | ||||
| + | ||||
|  	if (hw->version == 1) { | ||||
|  		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, | ||||
|  				   BIT(hw->index), 0); | ||||
| @@ -1373,8 +1572,10 @@ mtk_wed_attach(struct mtk_wed_device *dev) | ||||
|  	} | ||||
|   | ||||
|  out: | ||||
| -	if (ret) | ||||
| -		mtk_wed_detach(dev); | ||||
| +	if (ret) { | ||||
| +		dev_err(dev->hw->dev, "failed to attach wed device\n"); | ||||
| +		__mtk_wed_detach(dev); | ||||
| +	} | ||||
|  unlock: | ||||
|  	mutex_unlock(&hw_lock); | ||||
|   | ||||
| @@ -1382,7 +1583,8 @@ mtk_wed_attach(struct mtk_wed_device *dev) | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| -mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) | ||||
| +mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, | ||||
| +		void __iomem *regs, bool reset) | ||||
|  { | ||||
|  	struct mtk_wed_ring *ring = &dev->tx_ring[idx]; | ||||
|   | ||||
| @@ -1401,11 +1603,12 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) | ||||
|  	if (WARN_ON(idx >= ARRAY_SIZE(dev->tx_ring))) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE, | ||||
| -			       sizeof(*ring->desc), true)) | ||||
| +	if (!reset && mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE, | ||||
| +					 sizeof(*ring->desc), true)) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| -	if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) | ||||
| +	if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE, | ||||
| +				       reset)) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
|  	ring->reg_base = MTK_WED_RING_TX(idx); | ||||
| @@ -1450,18 +1653,20 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| -mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) | ||||
| +mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs, | ||||
| +		      bool reset) | ||||
|  { | ||||
|  	struct mtk_wed_ring *ring = &dev->rx_ring[idx]; | ||||
|   | ||||
|  	if (WARN_ON(idx >= ARRAY_SIZE(dev->rx_ring))) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	if (mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE, | ||||
| -			       sizeof(*ring->desc), false)) | ||||
| +	if (!reset && mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE, | ||||
| +					 sizeof(*ring->desc), false)) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| -	if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) | ||||
| +	if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE, | ||||
| +				       reset)) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
|  	ring->reg_base = MTK_WED_RING_RX_DATA(idx); | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed.h b/drivers/net/ethernet/mediatek/mtk_wed.h | ||||
| index 1bfd96f..2ce1a5b 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.h | ||||
| @@ -160,6 +160,9 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, | ||||
|  void mtk_wed_exit(void); | ||||
|  int mtk_wed_flow_add(int index); | ||||
|  void mtk_wed_flow_remove(int index); | ||||
| +void mtk_wed_fe_reset(void); | ||||
| +void mtk_wed_fe_reset_complete(void); | ||||
| + | ||||
|  #else | ||||
|  static inline void | ||||
|  mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, | ||||
| @@ -178,6 +181,13 @@ static inline int mtk_wed_flow_add(int index) | ||||
|  static inline void mtk_wed_flow_remove(int index) | ||||
|  { | ||||
|  } | ||||
| +static inline void mtk_wed_fe_reset(void) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
| +static inline void mtk_wed_fe_reset_complete(void) | ||||
| +{ | ||||
| +} | ||||
|  #endif | ||||
|   | ||||
|  #ifdef CONFIG_DEBUG_FS | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| index a79305f..645b8b1 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| @@ -32,11 +32,15 @@ struct mtk_wdma_desc { | ||||
|   | ||||
|  #define MTK_WED_RESET					0x008 | ||||
|  #define MTK_WED_RESET_TX_BM				BIT(0) | ||||
| +#define MTK_WED_RESET_RX_BM				BIT(1) | ||||
|  #define MTK_WED_RESET_TX_FREE_AGENT			BIT(4) | ||||
|  #define MTK_WED_RESET_WPDMA_TX_DRV			BIT(8) | ||||
|  #define MTK_WED_RESET_WPDMA_RX_DRV			BIT(9) | ||||
| +#define MTK_WED_RESET_WPDMA_RX_D_DRV			BIT(10) | ||||
|  #define MTK_WED_RESET_WPDMA_INT_AGENT			BIT(11) | ||||
|  #define MTK_WED_RESET_WED_TX_DMA			BIT(12) | ||||
| +#define MTK_WED_RESET_WED_RX_DMA			BIT(13) | ||||
| +#define MTK_WED_RESET_WDMA_TX_DRV			BIT(16) | ||||
|  #define MTK_WED_RESET_WDMA_RX_DRV			BIT(17) | ||||
|  #define MTK_WED_RESET_WDMA_INT_AGENT			BIT(19) | ||||
|  #define MTK_WED_RESET_RX_RRO_QM				BIT(20) | ||||
| @@ -174,6 +178,8 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_RESET_IDX				0x20c | ||||
|  #define MTK_WED_RESET_IDX_TX				GENMASK(3, 0) | ||||
|  #define MTK_WED_RESET_IDX_RX				GENMASK(17, 16) | ||||
| +#define MTK_WED_RESET_IDX_RX_V2				GENMASK(7, 6) | ||||
| +#define MTK_WED_RESET_WPDMA_IDX_RX			GENMASK(31, 30) | ||||
|   | ||||
|  #define MTK_WED_TX_MIB(_n)				(0x2a0 + (_n) * 4) | ||||
|  #define MTK_WED_RX_MIB(_n)				(0x2e0 + (_n) * 4) | ||||
| @@ -287,6 +293,9 @@ struct mtk_wdma_desc { | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_D_GLO_CFG			0x75c | ||||
|  #define MTK_WED_WPDMA_RX_D_RX_DRV_EN			BIT(0) | ||||
| +#define MTK_WED_WPDMA_RX_D_RX_DRV_BUSY			BIT(1) | ||||
| +#define MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE		BIT(3) | ||||
| +#define MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE		BIT(4) | ||||
|  #define MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL		GENMASK(11, 7) | ||||
|  #define MTK_WED_WPDMA_RX_D_RXD_READ_LEN			GENMASK(31, 24) | ||||
|   | ||||
| diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h | ||||
| index 658f392..6772ea8 100644 | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h | ||||
| @@ -151,16 +151,21 @@ struct mtk_wed_device { | ||||
|  		void (*release_rx_buf)(struct mtk_wed_device *wed); | ||||
|  		void (*update_wo_rx_stats)(struct mtk_wed_device *wed, | ||||
|  					   struct mtk_wed_wo_rx_stats *stats); | ||||
| +		int (*reset)(struct mtk_wed_device *wed); | ||||
| +		void (*reset_complete)(struct mtk_wed_device *wed); | ||||
|  	} wlan; | ||||
| +	struct completion fe_reset_done; | ||||
| +	struct completion wlan_reset_done; | ||||
| +	atomic_t fe_reset; | ||||
|  #endif | ||||
|  }; | ||||
|   | ||||
|  struct mtk_wed_ops { | ||||
|  	int (*attach)(struct mtk_wed_device *dev); | ||||
|  	int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring, | ||||
| -			     void __iomem *regs); | ||||
| +			     void __iomem *regs, bool reset); | ||||
|  	int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring, | ||||
| -			     void __iomem *regs); | ||||
| +			     void __iomem *regs, bool reset); | ||||
|  	int (*txfree_ring_setup)(struct mtk_wed_device *dev, | ||||
|  				 void __iomem *regs); | ||||
|  	int (*msg_update)(struct mtk_wed_device *dev, int cmd_id, | ||||
| @@ -216,8 +221,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev) | ||||
|  #define mtk_wed_device_active(_dev) !!(_dev)->ops | ||||
|  #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev) | ||||
|  #define mtk_wed_device_start(_dev, _mask) (_dev)->ops->start(_dev, _mask) | ||||
| -#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) \ | ||||
| -	(_dev)->ops->tx_ring_setup(_dev, _ring, _regs) | ||||
| +#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) \ | ||||
| +	(_dev)->ops->tx_ring_setup(_dev, _ring, _regs, _reset) | ||||
|  #define mtk_wed_device_txfree_ring_setup(_dev, _regs) \ | ||||
|  	(_dev)->ops->txfree_ring_setup(_dev, _regs) | ||||
|  #define mtk_wed_device_reg_read(_dev, _reg) \ | ||||
| @@ -228,12 +233,14 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev) | ||||
|  	(_dev)->ops->irq_get(_dev, _mask) | ||||
|  #define mtk_wed_device_irq_set_mask(_dev, _mask) \ | ||||
|  	(_dev)->ops->irq_set_mask(_dev, _mask) | ||||
| -#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) \ | ||||
| -	(_dev)->ops->rx_ring_setup(_dev, _ring, _regs) | ||||
| +#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) \ | ||||
| +	(_dev)->ops->rx_ring_setup(_dev, _ring, _regs, reset) | ||||
|  #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \ | ||||
|  	(_dev)->ops->ppe_check(_dev, _skb, _reason, _hash) | ||||
|  #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \ | ||||
|  	(_dev)->ops->msg_update(_dev, _id, _msg, _len) | ||||
| +#define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev) | ||||
| +#define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev) | ||||
|  #else | ||||
|  static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -241,15 +248,17 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) | ||||
|  } | ||||
|  #define mtk_wed_device_detach(_dev) do {} while (0) | ||||
|  #define mtk_wed_device_start(_dev, _mask) do {} while (0) | ||||
| -#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) -ENODEV | ||||
| +#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV | ||||
|  #define mtk_wed_device_txfree_ring_setup(_dev, _ring, _regs) -ENODEV | ||||
|  #define mtk_wed_device_reg_read(_dev, _reg) 0 | ||||
|  #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0) | ||||
|  #define mtk_wed_device_irq_get(_dev, _mask) 0 | ||||
|  #define mtk_wed_device_irq_set_mask(_dev, _mask) do {} while (0) | ||||
| -#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV | ||||
| +#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV | ||||
|  #define mtk_wed_device_ppe_check(_dev, _hash)  do {} while (0) | ||||
|  #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV | ||||
| +#define mtk_wed_device_stop(_dev) do {} while (0) | ||||
| +#define mtk_wed_device_dma_reset(_dev) do {} while (0) | ||||
|  #endif | ||||
|   | ||||
|  #endif | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,207 +0,0 @@ | ||||
| From 7feee53fdfd481fc2beb02739ccd0e87f1c96b7a Mon Sep 17 00:00:00 2001 | ||||
| From: Bc-bocun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 11:07:14 +0800 | ||||
| Subject: [PATCH 10/24] ethernet-update-ppe-backward-compatible-two-way-hash | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c | 10 ++++++++- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h |  1 + | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c     | 24 ++++++++++++++------- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.h     |  5 +++-- | ||||
|  4 files changed, 29 insertions(+), 11 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index a24b223..e8837b6 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -5799,7 +5799,8 @@ static int mtk_probe(struct platform_device *pdev) | ||||
|   | ||||
|  		for (i = 0; i < eth->ppe_num; i++) { | ||||
|  			eth->ppe[i] = mtk_ppe_init(eth, | ||||
| -					   eth->base + MTK_ETH_PPE_BASE + i * 0x400, 2, i); | ||||
| +						   eth->base + MTK_ETH_PPE_BASE + i * 0x400, | ||||
| +						   2, eth->soc->hash_way, i); | ||||
|  			if (!eth->ppe[i]) { | ||||
|  				err = -ENOMEM; | ||||
|  				goto err_free_dev; | ||||
| @@ -5913,6 +5914,7 @@ static const struct mtk_soc_data mt2701_data = { | ||||
|  	.required_clks = MT7623_CLKS_BITMAP, | ||||
|  	.required_pctl = true, | ||||
|  	.has_sram = false, | ||||
| +	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
|  	.txrx = { | ||||
| @@ -5931,6 +5933,7 @@ static const struct mtk_soc_data mt7621_data = { | ||||
|  	.required_clks = MT7621_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
|  	.txrx = { | ||||
| @@ -5950,6 +5953,7 @@ static const struct mtk_soc_data mt7622_data = { | ||||
|  	.required_clks = MT7622_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
|  	.txrx = { | ||||
| @@ -5968,6 +5972,7 @@ static const struct mtk_soc_data mt7623_data = { | ||||
|  	.required_clks = MT7623_CLKS_BITMAP, | ||||
|  	.required_pctl = true, | ||||
|  	.has_sram = false, | ||||
| +	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
|  	.txrx = { | ||||
| @@ -6005,6 +6010,7 @@ static const struct mtk_soc_data mt7986_data = { | ||||
|  	.required_clks = MT7986_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.hash_way = 4, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 4, | ||||
|  	.txrx = { | ||||
| @@ -6024,6 +6030,8 @@ static const struct mtk_soc_data mt7981_data = { | ||||
|  	.required_clks = MT7981_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.hash_way = 4, | ||||
| +	.offload_version = 2, | ||||
|  	.rss_num = 4, | ||||
|  	.txrx = { | ||||
|  		.txd_size = sizeof(struct mtk_tx_dma_v2), | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index 9099dea..b4f04e2 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -1732,6 +1732,7 @@ struct mtk_soc_data { | ||||
|  	u64		caps; | ||||
|  	u64		required_clks; | ||||
|  	bool		required_pctl; | ||||
| +	u8		hash_way; | ||||
|  	u8		offload_version; | ||||
|  	netdev_features_t hw_features; | ||||
|  	bool		has_sram; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index c9ee505..569bf34 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -88,7 +88,7 @@ static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable) | ||||
|  		enable * MTK_PPE_CACHE_CTL_EN); | ||||
|  } | ||||
|   | ||||
| -static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e) | ||||
| +static u32 mtk_ppe_hash_entry(struct mtk_ppe *ppe, struct mtk_foe_entry *e) | ||||
|  { | ||||
|  	u32 hv1, hv2, hv3; | ||||
|  	u32 hash; | ||||
| @@ -122,7 +122,7 @@ static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e) | ||||
|  	hash = (hash >> 24) | ((hash & 0xffffff) << 8); | ||||
|  	hash ^= hv1 ^ hv2 ^ hv3; | ||||
|  	hash ^= hash >> 16; | ||||
| -	hash <<= 2; | ||||
| +	hash <<= (ffs(ppe->way) - 1); | ||||
|  	hash &= MTK_PPE_ENTRIES - 1; | ||||
|   | ||||
|  	return hash; | ||||
| @@ -557,10 +557,10 @@ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  	if (type == MTK_PPE_PKT_TYPE_BRIDGE) | ||||
|  		return mtk_foe_entry_commit_l2(ppe, entry); | ||||
|   | ||||
| -	hash = mtk_ppe_hash_entry(&entry->data); | ||||
| +	hash = mtk_ppe_hash_entry(ppe, &entry->data); | ||||
|  	entry->hash = 0xffff; | ||||
|  	spin_lock_bh(&ppe_lock); | ||||
| -	hlist_add_head(&entry->list, &ppe->foe_flow[hash / 4]); | ||||
| +	hlist_add_head(&entry->list, &ppe->foe_flow[hash / ppe->way]); | ||||
|  	spin_unlock_bh(&ppe_lock); | ||||
|   | ||||
|  	return 0; | ||||
| @@ -584,7 +584,7 @@ mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, | ||||
|  	flow_info->l2_data.base_flow = entry; | ||||
|  	flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW; | ||||
|  	flow_info->hash = hash; | ||||
| -	hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 4]); | ||||
| +	hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / ppe->way]); | ||||
|  	hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); | ||||
|   | ||||
|  	hwe = &ppe->foe_table[hash]; | ||||
| @@ -608,7 +608,7 @@ mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, | ||||
|   | ||||
|  void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) | ||||
|  { | ||||
| -	struct hlist_head *head = &ppe->foe_flow[hash / 4]; | ||||
| +	struct hlist_head *head = &ppe->foe_flow[hash / ppe->way]; | ||||
|  	struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; | ||||
|  	struct mtk_flow_entry *entry; | ||||
|  	struct mtk_foe_bridge key = {}; | ||||
| @@ -695,12 +695,12 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); | ||||
|  } | ||||
|   | ||||
| -struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, | ||||
| -		 int version, int id) | ||||
| +struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int way, int id) | ||||
|  { | ||||
|  	struct device *dev = eth->dev; | ||||
|  	struct mtk_foe_entry *foe; | ||||
|  	struct mtk_ppe *ppe; | ||||
| +	struct hlist_head *flow; | ||||
|   | ||||
|  	ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL); | ||||
|  	if (!ppe) | ||||
| @@ -715,6 +715,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, | ||||
|  	ppe->eth = eth; | ||||
|  	ppe->dev = dev; | ||||
|  	ppe->version = version; | ||||
| +	ppe->way = way; | ||||
|  	ppe->id = id; | ||||
|   | ||||
|  	foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe), | ||||
| @@ -724,6 +725,13 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, | ||||
|   | ||||
|  	ppe->foe_table = foe; | ||||
|   | ||||
| +	flow = devm_kzalloc(dev, (MTK_PPE_ENTRIES / way) * sizeof(*flow), | ||||
| +			    GFP_KERNEL); | ||||
| +	if (!flow) | ||||
| +		return NULL; | ||||
| + | ||||
| +	ppe->foe_flow = flow; | ||||
| + | ||||
|  	return ppe; | ||||
|  } | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| index 86bbac8..feb1a4a 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -322,19 +322,20 @@ struct mtk_ppe { | ||||
|  	void __iomem *base; | ||||
|  	int version; | ||||
|  	int id; | ||||
| +	int way; | ||||
|   | ||||
|  	struct mtk_foe_entry *foe_table; | ||||
|  	dma_addr_t foe_phys; | ||||
|   | ||||
|  	u16 foe_check_time[MTK_PPE_ENTRIES]; | ||||
| -	struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2]; | ||||
| +	struct hlist_head *foe_flow; | ||||
|   | ||||
|  	struct rhashtable l2_flows; | ||||
|   | ||||
|  	void *acct_table; | ||||
|  }; | ||||
|   | ||||
| -struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int id); | ||||
| +struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int way, int id); | ||||
|  int mtk_ppe_start(struct mtk_ppe *ppe); | ||||
|  int mtk_ppe_stop(struct mtk_ppe *ppe); | ||||
|   | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,463 +0,0 @@ | ||||
| From 4eaba588e0c730d6188f5f1b667a55d1b9ca0fe6 Mon Sep 17 00:00:00 2001 | ||||
| From: Bc-bocun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 11:09:23 +0800 | ||||
| Subject: [PATCH 11/24] flow-offload-add-mtkhnat-flow-accounting | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c   |  11 +- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   1 + | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c       | 131 +++++++++++++++++- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.h       |  23 ++- | ||||
|  .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |  10 +- | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   |   7 + | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  14 ++ | ||||
|  net/netfilter/xt_FLOWOFFLOAD.c                |   2 +- | ||||
|  8 files changed, 191 insertions(+), 8 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index e8837b6..9cd306d 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -5800,7 +5800,8 @@ static int mtk_probe(struct platform_device *pdev) | ||||
|  		for (i = 0; i < eth->ppe_num; i++) { | ||||
|  			eth->ppe[i] = mtk_ppe_init(eth, | ||||
|  						   eth->base + MTK_ETH_PPE_BASE + i * 0x400, | ||||
| -						   2, eth->soc->hash_way, i); | ||||
| +						   2, eth->soc->hash_way, i, | ||||
| +						   eth->soc->has_accounting); | ||||
|  			if (!eth->ppe[i]) { | ||||
|  				err = -ENOMEM; | ||||
|  				goto err_free_dev; | ||||
| @@ -5914,6 +5915,7 @@ static const struct mtk_soc_data mt2701_data = { | ||||
|  	.required_clks = MT7623_CLKS_BITMAP, | ||||
|  	.required_pctl = true, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = false, | ||||
|  	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
| @@ -5933,6 +5935,7 @@ static const struct mtk_soc_data mt7621_data = { | ||||
|  	.required_clks = MT7621_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = false, | ||||
|  	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
| @@ -5953,6 +5956,7 @@ static const struct mtk_soc_data mt7622_data = { | ||||
|  	.required_clks = MT7622_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = true, | ||||
|  	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
| @@ -5972,6 +5976,7 @@ static const struct mtk_soc_data mt7623_data = { | ||||
|  	.required_clks = MT7623_CLKS_BITMAP, | ||||
|  	.required_pctl = true, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = false, | ||||
|  	.hash_way = 2, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 0, | ||||
| @@ -5992,6 +5997,7 @@ static const struct mtk_soc_data mt7629_data = { | ||||
|  	.required_clks = MT7629_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = true, | ||||
|  	.rss_num = 0, | ||||
|  	.txrx = { | ||||
|  		.txd_size = sizeof(struct mtk_tx_dma), | ||||
| @@ -6010,6 +6016,7 @@ static const struct mtk_soc_data mt7986_data = { | ||||
|  	.required_clks = MT7986_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = true, | ||||
|  	.hash_way = 4, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 4, | ||||
| @@ -6030,6 +6037,7 @@ static const struct mtk_soc_data mt7981_data = { | ||||
|  	.required_clks = MT7981_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = true, | ||||
|  	.hash_way = 4, | ||||
|  	.offload_version = 2, | ||||
|  	.rss_num = 4, | ||||
| @@ -6067,6 +6075,7 @@ static const struct mtk_soc_data rt5350_data = { | ||||
|  	.required_clks = MT7628_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = false, | ||||
| +	.has_accounting = false, | ||||
|  	.rss_num = 0, | ||||
|  	.txrx = { | ||||
|  		.txd_size = sizeof(struct mtk_tx_dma), | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index b4f04e2..5f90765 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -1736,6 +1736,7 @@ struct mtk_soc_data { | ||||
|  	u8		offload_version; | ||||
|  	netdev_features_t hw_features; | ||||
|  	bool		has_sram; | ||||
| +	bool		has_accounting; | ||||
|  	struct { | ||||
|  		u32	txd_size; | ||||
|  		u32	rxd_size; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index 569bf34..94e03b2 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -74,6 +74,46 @@ static int mtk_ppe_wait_busy(struct mtk_ppe *ppe) | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	u32 val; | ||||
| + | ||||
| +	ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val, | ||||
| +				 !(val & MTK_PPE_MIB_SER_CR_ST), | ||||
| +				 20, MTK_PPE_WAIT_TIMEOUT_US); | ||||
| + | ||||
| +	if (ret) | ||||
| +		dev_err(ppe->dev, "MIB table busy"); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets) | ||||
| +{ | ||||
| +	u32 val, cnt_r0, cnt_r1, cnt_r2; | ||||
| +	u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high; | ||||
| + | ||||
| +	val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST; | ||||
| +	ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val); | ||||
| + | ||||
| +	if (mtk_ppe_mib_wait_busy(ppe)) | ||||
| +		return -ETIMEDOUT; | ||||
| + | ||||
| +	cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0); | ||||
| +	cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); | ||||
| +	cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2); | ||||
| + | ||||
| +	byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0); | ||||
| +	byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1); | ||||
| +	pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1); | ||||
| +	pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2); | ||||
| +	*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low; | ||||
| +	*packets = (pkt_cnt_high << 16) | pkt_cnt_low; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) | ||||
|  { | ||||
|  	ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR); | ||||
| @@ -426,6 +466,18 @@ __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  							      MTK_FOE_STATE_INVALID); | ||||
|  		dma_wmb(); | ||||
|  		mtk_ppe_cache_clear(ppe); | ||||
| + | ||||
| +		if (ppe->accounting) { | ||||
| +			struct mtk_foe_accounting *acct, *acct_updated; | ||||
| + | ||||
| +			acct = ppe->acct_table + entry->hash * sizeof(*acct); | ||||
| +			acct->packets = 0; | ||||
| +			acct->bytes = 0; | ||||
| + | ||||
| +			acct_updated = ppe->acct_updated_table + entry->hash * sizeof(*acct_updated); | ||||
| +			acct_updated->packets = 0; | ||||
| +			acct_updated->bytes = 0; | ||||
| +		} | ||||
|  	} | ||||
|  	entry->hash = 0xffff; | ||||
|   | ||||
| @@ -528,6 +580,16 @@ __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, | ||||
|  	wmb(); | ||||
|  	hwe->ib1 = entry->ib1; | ||||
|   | ||||
| +	if (ppe->accounting) { | ||||
| +		int type; | ||||
| + | ||||
| +		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); | ||||
| +		if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) | ||||
| +			hwe->ipv6.ib2 |= MTK_FOE_IB2_MIB_CNT; | ||||
| +		else | ||||
| +			hwe->ipv4.ib2 |= MTK_FOE_IB2_MIB_CNT; | ||||
| +	} | ||||
| + | ||||
|  	dma_wmb(); | ||||
|   | ||||
|  	mtk_ppe_cache_clear(ppe); | ||||
| @@ -637,8 +699,6 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) | ||||
|  		} | ||||
|   | ||||
|  		if (found || !mtk_flow_entry_match(entry, hwe)) { | ||||
| -			if (entry->hash != 0xffff) | ||||
| -				entry->hash = 0xffff; | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| @@ -695,12 +755,44 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); | ||||
|  } | ||||
|   | ||||
| -struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int way, int id) | ||||
| +struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, struct mtk_foe_accounting *diff) | ||||
| +{ | ||||
| +	struct mtk_foe_accounting *acct, *acct_updated; | ||||
| +	int size = sizeof(struct mtk_foe_accounting); | ||||
| +	u64 bytes, packets; | ||||
| + | ||||
| +	if (!ppe->accounting) | ||||
| +		return NULL; | ||||
| + | ||||
| +	if (mtk_mib_entry_read(ppe, index, &bytes, &packets)) | ||||
| +		return NULL; | ||||
| + | ||||
| +	acct = ppe->acct_table + index * size; | ||||
| + | ||||
| +	acct->bytes += bytes; | ||||
| +	acct->packets += packets; | ||||
| + | ||||
| +	if (diff) { | ||||
| +		acct_updated = ppe->acct_updated_table + index * size; | ||||
| + | ||||
| +		diff->bytes = acct->bytes - acct_updated->bytes; | ||||
| +		diff->packets = acct->packets - acct_updated->packets; | ||||
| +		acct_updated->bytes += diff->bytes; | ||||
| +		acct_updated->packets += diff->packets; | ||||
| +	} | ||||
| + | ||||
| +	return acct; | ||||
| +} | ||||
| + | ||||
| +struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int way, int id, | ||||
| +			     int accounting) | ||||
|  { | ||||
|  	struct device *dev = eth->dev; | ||||
|  	struct mtk_foe_entry *foe; | ||||
| +	struct mtk_mib_entry *mib; | ||||
|  	struct mtk_ppe *ppe; | ||||
|  	struct hlist_head *flow; | ||||
| +	struct mtk_foe_accounting *acct, *acct_updated; | ||||
|   | ||||
|  	ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL); | ||||
|  	if (!ppe) | ||||
| @@ -717,6 +809,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int versio | ||||
|  	ppe->version = version; | ||||
|  	ppe->way = way; | ||||
|  	ppe->id = id; | ||||
| +	ppe->accounting = accounting; | ||||
|   | ||||
|  	foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe), | ||||
|  				  &ppe->foe_phys, GFP_KERNEL); | ||||
| @@ -732,6 +825,31 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int versio | ||||
|   | ||||
|  	ppe->foe_flow = flow; | ||||
|   | ||||
| +	if (accounting) { | ||||
| +		mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib), | ||||
| +				  &ppe->mib_phys, GFP_KERNEL); | ||||
| +		if (!foe) | ||||
| +			return NULL; | ||||
| + | ||||
| +		memset(mib, 0, MTK_PPE_ENTRIES * sizeof(*mib)); | ||||
| + | ||||
| +		ppe->mib_table = mib; | ||||
| + | ||||
| +		acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct), | ||||
| +				    GFP_KERNEL); | ||||
| +		if (!acct) | ||||
| +			return NULL; | ||||
| + | ||||
| +		ppe->acct_table = acct; | ||||
| + | ||||
| +		acct_updated = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct_updated), | ||||
| +					    GFP_KERNEL); | ||||
| +		if (!acct_updated) | ||||
| +			return NULL; | ||||
| + | ||||
| +		ppe->acct_updated_table = acct_updated; | ||||
| +	} | ||||
| + | ||||
|  	return ppe; | ||||
|  } | ||||
|   | ||||
| @@ -834,6 +952,13 @@ int mtk_ppe_start(struct mtk_ppe *ppe) | ||||
|  	ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f); | ||||
|  #endif | ||||
|   | ||||
| +	if (ppe->accounting && ppe->mib_phys) { | ||||
| +		ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys); | ||||
| +		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN, MTK_PPE_MIB_CFG_EN); | ||||
| +		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR, MTK_PPE_MIB_CFG_RD_CLR); | ||||
| +		ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN, MTK_PPE_MIB_CACHE_CTL_EN); | ||||
| +	} | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| index feb1a4a..86288b0 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -316,6 +316,20 @@ struct mtk_flow_entry { | ||||
|  	unsigned long cookie; | ||||
|  }; | ||||
|   | ||||
| +struct mtk_mib_entry { | ||||
| +	u32	byt_cnt_l; | ||||
| +	u16	byt_cnt_h; | ||||
| +	u32	pkt_cnt_l; | ||||
| +	u8	pkt_cnt_h; | ||||
| +	u8	_rsv0; | ||||
| +	u32	_rsv1; | ||||
| +} __packed; | ||||
| + | ||||
| +struct mtk_foe_accounting { | ||||
| +	u64	bytes; | ||||
| +	u64	packets; | ||||
| +}; | ||||
| + | ||||
|  struct mtk_ppe { | ||||
|  	struct mtk_eth *eth; | ||||
|  	struct device *dev; | ||||
| @@ -323,19 +337,25 @@ struct mtk_ppe { | ||||
|  	int version; | ||||
|  	int id; | ||||
|  	int way; | ||||
| +	int accounting; | ||||
|   | ||||
|  	struct mtk_foe_entry *foe_table; | ||||
|  	dma_addr_t foe_phys; | ||||
|   | ||||
| +	struct mtk_mib_entry *mib_table; | ||||
| +	dma_addr_t mib_phys; | ||||
| + | ||||
|  	u16 foe_check_time[MTK_PPE_ENTRIES]; | ||||
|  	struct hlist_head *foe_flow; | ||||
|   | ||||
|  	struct rhashtable l2_flows; | ||||
|   | ||||
|  	void *acct_table; | ||||
| +	void *acct_updated_table; | ||||
|  }; | ||||
|   | ||||
| -struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int way, int id); | ||||
| +struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version, int way, int id, | ||||
| +			     int accounting); | ||||
|  int mtk_ppe_start(struct mtk_ppe *ppe); | ||||
|  int mtk_ppe_stop(struct mtk_ppe *ppe); | ||||
|   | ||||
| @@ -386,5 +406,6 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
| +struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, struct mtk_foe_accounting *diff); | ||||
|   | ||||
|  #endif | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| index f4ebe59..d713e2e 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| @@ -81,6 +81,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, struct mtk_ppe *ppe, bool bind) | ||||
|  		struct mtk_foe_entry *entry = &ppe->foe_table[i]; | ||||
|  		struct mtk_foe_mac_info *l2; | ||||
|  		struct mtk_flow_addr_info ai = {}; | ||||
| +		struct mtk_foe_accounting *acct; | ||||
|  		unsigned char h_source[ETH_ALEN]; | ||||
|  		unsigned char h_dest[ETH_ALEN]; | ||||
|  		int type, state; | ||||
| @@ -94,6 +95,8 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, struct mtk_ppe *ppe, bool bind) | ||||
|  		if (bind && state != MTK_FOE_STATE_BIND) | ||||
|  			continue; | ||||
|   | ||||
| +		acct = mtk_foe_entry_get_mib(ppe, i, NULL); | ||||
| + | ||||
|  		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); | ||||
|  		seq_printf(m, "%05x %s %7s", i, | ||||
|  			   mtk_foe_entry_state_str(state), | ||||
| @@ -154,9 +157,12 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, struct mtk_ppe *ppe, bool bind) | ||||
|  		*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo); | ||||
|   | ||||
|  		seq_printf(m, " eth=%pM->%pM etype=%04x" | ||||
| -			      " vlan=%d,%d ib1=%08x ib2=%08x\n", | ||||
| +			      " vlan=%d,%d ib1=%08x ib2=%08x" | ||||
| +			      " packets=%lld bytes=%lld\n", | ||||
|  			   h_source, h_dest, ntohs(l2->etype), | ||||
| -			   l2->vlan1, l2->vlan2, entry->ib1, ib2); | ||||
| +			   l2->vlan1, l2->vlan2, entry->ib1, ib2, | ||||
| +			   acct->packets, acct->bytes | ||||
| +			   ); | ||||
|  	} | ||||
|   | ||||
|  	return 0; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| index f256607..b80f72d 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -509,6 +509,7 @@ static int | ||||
|  mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  { | ||||
|  	struct mtk_flow_entry *entry; | ||||
| +	struct mtk_foe_accounting diff; | ||||
|  	u32 idle; | ||||
|  	int i; | ||||
|   | ||||
| @@ -521,6 +522,12 @@ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	idle = mtk_foe_entry_idle_time(eth->ppe[i], entry); | ||||
|  	f->stats.lastused = jiffies - idle * HZ; | ||||
|   | ||||
| +	if (entry->hash != 0xFFFF) { | ||||
| +		mtk_foe_entry_get_mib(eth->ppe[i], entry->hash, &diff); | ||||
| +		f->stats.pkts += diff.packets; | ||||
| +		f->stats.bytes += diff.bytes; | ||||
| +	} | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| index d319f18..8d3ebe1 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| @@ -145,6 +145,20 @@ enum { | ||||
|   | ||||
|  #define MTK_PPE_MIB_TB_BASE			0x338 | ||||
|   | ||||
| +#define MTK_PPE_MIB_SER_CR			0x33C | ||||
| +#define MTK_PPE_MIB_SER_CR_ST			BIT(16) | ||||
| +#define MTK_PPE_MIB_SER_CR_ADDR			GENMASK(13, 0) | ||||
| + | ||||
| +#define MTK_PPE_MIB_SER_R0			0x340 | ||||
| +#define MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW		GENMASK(31, 0) | ||||
| + | ||||
| +#define MTK_PPE_MIB_SER_R1			0x344 | ||||
| +#define MTK_PPE_MIB_SER_R1_PKT_CNT_LOW		GENMASK(31, 16) | ||||
| +#define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH	GENMASK(15, 0) | ||||
| + | ||||
| +#define MTK_PPE_MIB_SER_R2			0x348 | ||||
| +#define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH		GENMASK(23, 0) | ||||
| + | ||||
|  #define MTK_PPE_MIB_CACHE_CTL			0x350 | ||||
|  #define MTK_PPE_MIB_CACHE_CTL_EN		BIT(0) | ||||
|  #define MTK_PPE_MIB_CACHE_CTL_FLUSH		BIT(2) | ||||
| diff --git a/net/netfilter/xt_FLOWOFFLOAD.c b/net/netfilter/xt_FLOWOFFLOAD.c | ||||
| index e4c7db9..aae37f5 100644 | ||||
| --- a/net/netfilter/xt_FLOWOFFLOAD.c | ||||
| +++ b/net/netfilter/xt_FLOWOFFLOAD.c | ||||
| @@ -772,7 +772,7 @@ static int __init xt_flowoffload_tg_init(void) | ||||
|  	if (ret) | ||||
|  		goto cleanup; | ||||
|   | ||||
| -	flowtable[1].ft.flags = NF_FLOWTABLE_HW_OFFLOAD; | ||||
| +	flowtable[1].ft.flags = NF_FLOWTABLE_HW_OFFLOAD | NF_FLOWTABLE_COUNTER; | ||||
|   | ||||
|  	ret = xt_register_target(&offload_tg_reg); | ||||
|  	if (ret) | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,862 +0,0 @@ | ||||
| From 51573e91708521bdc47a9d6f771f2cbde11e5ed7 Mon Sep 17 00:00:00 2001 | ||||
| From: "chak-kei.lam" <chak-kei.lam@mediatek.com> | ||||
| Date: Tue, 9 Apr 2024 15:05:24 +0800 | ||||
| Subject: [PATCH 12/24] flow-offload-add-mtkhnat-qdma-qos | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/Makefile        |   2 +- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c   |  10 + | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  50 ++ | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c       |  48 +- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.h       |   4 + | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   |  28 +- | ||||
|  .../net/ethernet/mediatek/mtk_qdma_debugfs.c  | 448 ++++++++++++++++++ | ||||
|  include/net/flow_offload.h                    |   1 + | ||||
|  net/netfilter/nf_flow_table_offload.c         |   4 +- | ||||
|  9 files changed, 590 insertions(+), 5 deletions(-) | ||||
|  create mode 100644 drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile | ||||
| index fdbb90f..c7d2296 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/Makefile | ||||
| +++ b/drivers/net/ethernet/mediatek/Makefile | ||||
| @@ -5,7 +5,7 @@ | ||||
|   | ||||
|  obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth.o | ||||
|  mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o	\ | ||||
| -	     mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o | ||||
| +	     mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o  mtk_qdma_debugfs.o | ||||
|  mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o | ||||
|  ifdef CONFIG_DEBUG_FS | ||||
|  mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index 60672c6..8e3a276 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -5838,6 +5838,8 @@ static int mtk_probe(struct platform_device *pdev) | ||||
|  		} | ||||
|   | ||||
|  		mtk_ppe_debugfs_init(eth); | ||||
| + | ||||
| +		mtk_qdma_debugfs_init(eth); | ||||
|  	} | ||||
|   | ||||
|  	for (i = 0; i < MTK_MAX_DEVS; i++) { | ||||
| @@ -5953,6 +5955,7 @@ static const struct mtk_soc_data mt2701_data = { | ||||
|  		.rx_dma_l4_valid = RX_DMA_L4_VALID, | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT, | ||||
| +		.qdma_tx_sch = 2, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| @@ -5976,6 +5979,7 @@ static const struct mtk_soc_data mt7621_data = { | ||||
|  		.rxd_size = sizeof(struct mtk_rx_dma), | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT, | ||||
| +		.qdma_tx_sch = 2, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| @@ -6000,6 +6004,7 @@ static const struct mtk_soc_data mt7622_data = { | ||||
|  		.rx_dma_l4_valid = RX_DMA_L4_VALID, | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT, | ||||
| +		.qdma_tx_sch = 2, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| @@ -6023,6 +6028,7 @@ static const struct mtk_soc_data mt7623_data = { | ||||
|  		.rx_dma_l4_valid = RX_DMA_L4_VALID, | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT, | ||||
| +		.qdma_tx_sch = 2, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| @@ -6069,6 +6075,7 @@ static const struct mtk_soc_data mt7986_data = { | ||||
|  		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2, | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2, | ||||
| +		.qdma_tx_sch = 4, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| @@ -6093,6 +6100,7 @@ static const struct mtk_soc_data mt7981_data = { | ||||
|  		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2, | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2, | ||||
| +		.qdma_tx_sch = 4, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| @@ -6117,6 +6125,7 @@ static const struct mtk_soc_data mt7988_data = { | ||||
|  		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2, | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2, | ||||
| +		.qdma_tx_sch = 4, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| @@ -6138,6 +6147,7 @@ static const struct mtk_soc_data rt5350_data = { | ||||
|  		.rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA, | ||||
|  		.dma_max_len = MTK_TX_DMA_BUF_LEN, | ||||
|  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT, | ||||
| +		.qdma_tx_sch = 4, | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index 52e4b85..7b3230e 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -495,6 +495,9 @@ | ||||
|  #define FC_THRES_DROP_EN	(7 << 16) | ||||
|  #define FC_THRES_MIN		0x4444 | ||||
|   | ||||
| +/* QDMA TX Scheduler Rate Control Register */ | ||||
| +#define MTK_QDMA_TX_2SCH_BASE	(QDMA_BASE + 0x214) | ||||
| + | ||||
|  /* QDMA Interrupt Status Register */ | ||||
|  #define MTK_QDMA_INT_STATUS	(QDMA_BASE + 0x218) | ||||
|  #if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| @@ -538,6 +541,11 @@ | ||||
|  /* QDMA Interrupt Mask Register */ | ||||
|  #define MTK_QDMA_HRED2		(QDMA_BASE + 0x244) | ||||
|   | ||||
| +/* QDMA TX Queue MIB Interface Register */ | ||||
| +#define MTK_QTX_MIB_IF		(QDMA_BASE + 0x2bc) | ||||
| +#define MTK_MIB_ON_QTX_CFG	BIT(31) | ||||
| +#define MTK_VQTX_MIB_EN		BIT(28) | ||||
| + | ||||
|  /* QDMA TX Forward CPU Pointer Register */ | ||||
|  #define MTK_QTX_CTX_PTR		(QDMA_BASE +0x300) | ||||
|   | ||||
| @@ -565,6 +573,14 @@ | ||||
|  /* QDMA FQ Free Page Buffer Length Register */ | ||||
|  #define MTK_QDMA_FQ_BLEN	(QDMA_BASE +0x32c) | ||||
|   | ||||
| +/* QDMA TX Scheduler Rate Control Register */ | ||||
| +#define MTK_QDMA_TX_4SCH_BASE(x)	(QDMA_BASE + 0x398 + (((x) >> 1) * 0x4)) | ||||
| +#define MTK_QDMA_TX_SCH_MASK		GENMASK(15, 0) | ||||
| +#define MTK_QDMA_TX_SCH_MAX_WFQ		BIT(15) | ||||
| +#define MTK_QDMA_TX_SCH_RATE_EN		BIT(11) | ||||
| +#define MTK_QDMA_TX_SCH_RATE_MAN	GENMASK(10, 4) | ||||
| +#define MTK_QDMA_TX_SCH_RATE_EXP	GENMASK(3, 0) | ||||
| + | ||||
|  /* WDMA Registers */ | ||||
|  #define MTK_WDMA_CTX_PTR(x)	(WDMA_BASE(x) + 0x8) | ||||
|  #define MTK_WDMA_DTX_PTR(x)	(WDMA_BASE(x) + 0xC) | ||||
| @@ -1753,6 +1769,7 @@ struct mtk_soc_data { | ||||
|  		u32	rx_dma_l4_valid; | ||||
|  		u32	dma_max_len; | ||||
|  		u32	dma_len_offset; | ||||
| +		u32	qdma_tx_sch; | ||||
|  	} txrx; | ||||
|  }; | ||||
|   | ||||
| @@ -1946,6 +1963,7 @@ struct mtk_eth { | ||||
|  	spinlock_t			syscfg0_lock; | ||||
|  	struct timer_list		mtk_dma_monitor_timer; | ||||
|   | ||||
| +	u8				qos_toggle; | ||||
|  	u8				ppe_num; | ||||
|  	struct mtk_ppe			*ppe[MTK_MAX_PPE_NUM]; | ||||
|  	struct rhashtable		flow_table; | ||||
| @@ -2004,6 +2022,36 @@ extern const struct of_device_id of_mtk_match[]; | ||||
|  extern u32 mtk_hwlro_stats_ebl; | ||||
|  extern u32 dbg_show_level; | ||||
|   | ||||
| +static inline void mtk_set_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe, u32 val) | ||||
| +{ | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	foe->ib1 |= FIELD_PREP(MTK_FOE_IB1_UNBIND_SRC_PORT, val); | ||||
| +#endif | ||||
| +} | ||||
| + | ||||
| +static inline u32 mtk_get_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe) | ||||
| +{ | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +	return FIELD_GET(MTK_FOE_IB1_UNBIND_SRC_PORT, foe->ib1); | ||||
| +#else | ||||
| +	return 0; | ||||
| +#endif | ||||
| +} | ||||
| + | ||||
| +static inline int | ||||
| +mtk_ppe_check_pppq_path(struct mtk_eth *eth, struct mtk_foe_entry *foe, int dsa_port) | ||||
| +{ | ||||
| +	u32 sp = mtk_get_ib1_sp(eth, foe); | ||||
| + | ||||
| +	if ((dsa_port >= 0 && dsa_port <= 4) || | ||||
| +	    (dsa_port == 5 && (sp == PSE_WDMA0_PORT || | ||||
| +			       sp == PSE_WDMA1_PORT || | ||||
| +			       sp == PSE_WDMA2_PORT))) | ||||
| +		return 1; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  /* read the hardware status register */ | ||||
|  void mtk_stats_update_mac(struct mtk_mac *mac); | ||||
|   | ||||
| @@ -2037,4 +2085,6 @@ void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); | ||||
|  u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index); | ||||
|   | ||||
|  int mtk_ppe_debugfs_init(struct mtk_eth *eth); | ||||
| + | ||||
| +int mtk_qdma_debugfs_init(struct mtk_eth *eth); | ||||
|  #endif /* MTK_ETH_H */ | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index 7b92fff..f5dbfe9 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -139,7 +139,7 @@ static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable) | ||||
|  		enable * MTK_PPE_CACHE_CTL_EN); | ||||
|  } | ||||
|   | ||||
| -static u32 mtk_ppe_hash_entry(struct mtk_ppe *ppe, struct mtk_foe_entry *e) | ||||
| +u32 mtk_ppe_hash_entry(struct mtk_ppe *ppe, struct mtk_foe_entry *e) | ||||
|  { | ||||
|  	u32 hv1, hv2, hv3; | ||||
|  	u32 hash; | ||||
| @@ -431,12 +431,58 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid) | ||||
| +{ | ||||
| +	u32 *ib2 = mtk_foe_entry_ib2(entry); | ||||
| + | ||||
| +	*ib2 &= ~MTK_FOE_IB2_QID; | ||||
| +	*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, qid); | ||||
| +	*ib2 |= MTK_FOE_IB2_PSE_QOS; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
|  static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry) | ||||
|  { | ||||
|  	return !(entry->ib1 & MTK_FOE_IB1_STATIC) && | ||||
|  	       FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND; | ||||
|  } | ||||
|   | ||||
| +bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data) | ||||
| +{ | ||||
| +	int type, len; | ||||
| + | ||||
| +	if ((data->ib1 ^ entry->ib1) & MTK_FOE_IB1_UDP) | ||||
| +		return false; | ||||
| + | ||||
| +	type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); | ||||
| +	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) | ||||
| +		len = offsetof(struct mtk_foe_entry, ipv6._rsv); | ||||
| +	else | ||||
| +		len = offsetof(struct mtk_foe_entry, ipv4.ib2); | ||||
| + | ||||
| +	return !memcmp(&entry->data, &data->data, len - 4); | ||||
| +} | ||||
| + | ||||
| +int mtk_foe_entry_set_sp(struct mtk_ppe *ppe, struct mtk_foe_entry *entry) | ||||
| +{ | ||||
| +	struct mtk_foe_entry *hwe; | ||||
| +	u32 hash, sp = 0; | ||||
| +	int i; | ||||
| + | ||||
| +	hash = mtk_ppe_hash_entry(ppe, entry); | ||||
| +	for (i = 0; i < ppe->way; i++) { | ||||
| +		hwe = &ppe->foe_table[hash + i]; | ||||
| +		if (mtk_foe_entry_match(hwe, entry)) { | ||||
| +			sp = mtk_get_ib1_sp(ppe->eth, hwe); | ||||
| +			break; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	mtk_set_ib1_sp(ppe->eth, entry, sp); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static bool | ||||
|  mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data) | ||||
|  { | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| index bc48bd9..5529d64 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -429,9 +429,13 @@ int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid); | ||||
|  int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid); | ||||
|  int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|  			   int bss, int wcid); | ||||
| +int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid); | ||||
| +bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data); | ||||
| +int mtk_foe_entry_set_sp(struct mtk_ppe *ppe, struct mtk_foe_entry *entry); | ||||
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, struct mtk_foe_accounting *diff); | ||||
| +u32 mtk_ppe_hash_entry(struct mtk_ppe *ppe, struct mtk_foe_entry *e); | ||||
|   | ||||
|  #endif | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| index f9cd2f9..f0c63da 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -9,6 +9,8 @@ | ||||
|  #include <linux/ipv6.h> | ||||
|  #include <net/flow_offload.h> | ||||
|  #include <net/pkt_cls.h> | ||||
| +#include <net/netfilter/nf_conntrack.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
|  #include <net/dsa.h> | ||||
|  #include "mtk_eth_soc.h" | ||||
|  #include "mtk_wed.h" | ||||
| @@ -183,7 +185,7 @@ mtk_flow_get_dsa_port(struct net_device **dev) | ||||
|   | ||||
|  static int | ||||
|  mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, | ||||
| -			   struct net_device *dev, const u8 *dest_mac, | ||||
| +			   struct net_device *dev, struct nf_conn *ct, const u8 *dest_mac, | ||||
|  			   int *wed_index) | ||||
|  { | ||||
|  	struct mtk_wdma_info info = {}; | ||||
| @@ -209,6 +211,9 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, | ||||
|  	if (dsa_port >= 0) | ||||
|  		mtk_foe_entry_set_dsa(foe, dsa_port); | ||||
|   | ||||
| +	if (eth->qos_toggle == 2 && mtk_ppe_check_pppq_path(eth, foe, dsa_port)) | ||||
| +		mtk_foe_entry_set_qid(foe, dsa_port & MTK_QDMA_TX_MASK); | ||||
| + | ||||
|  	if (dev == eth->netdev[0]) | ||||
|  		pse_port = PSE_GDM1_PORT; | ||||
|  	else if (dev == eth->netdev[1]) | ||||
| @@ -219,6 +224,23 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, | ||||
|  		return -EOPNOTSUPP; | ||||
|   | ||||
|  out: | ||||
| +	if (eth->qos_toggle == 1 || (ct->mark & MTK_QDMA_TX_MASK) >= 6) { | ||||
| +		u8 qos_ul_toggle; | ||||
| + | ||||
| +		if (eth->qos_toggle == 2) | ||||
| +			qos_ul_toggle = ((ct->mark >> 16) & MTK_QDMA_TX_MASK) >= 6 ? 1 : 0; | ||||
| +		else | ||||
| +			qos_ul_toggle = ((ct->mark >> 16) & MTK_QDMA_TX_MASK) >= 1 ? 1 : 0; | ||||
| + | ||||
| +		if (qos_ul_toggle == 1) { | ||||
| +			if (dev == eth->netdev[1]) | ||||
| +				mtk_foe_entry_set_qid(foe, (ct->mark >> 16) & MTK_QDMA_TX_MASK); | ||||
| +			else | ||||
| +				mtk_foe_entry_set_qid(foe, ct->mark & MTK_QDMA_TX_MASK); | ||||
| +		} else | ||||
| +			mtk_foe_entry_set_qid(foe, ct->mark & MTK_QDMA_TX_MASK); | ||||
| +	} | ||||
| + | ||||
|  	mtk_foe_entry_set_pse_port(foe, pse_port); | ||||
|   | ||||
|  	return 0; | ||||
| @@ -449,7 +471,9 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	if (data.pppoe.num == 1) | ||||
|  		mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid); | ||||
|   | ||||
| -	err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest, | ||||
| +	mtk_foe_entry_set_sp(eth->ppe[ppe_index], &foe); | ||||
| + | ||||
| +	err = mtk_flow_set_output_device(eth, &foe, odev, f->flow->ct, data.eth.h_dest, | ||||
|  					 &wed_index); | ||||
|  	if (err) | ||||
|  		return err; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c b/drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c | ||||
| new file mode 100644 | ||||
| index 0000000..c7af3eb | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c | ||||
| @@ -0,0 +1,448 @@ | ||||
| +/* SPDX-License-Identifier: GPL-2.0 | ||||
| + * | ||||
| + * Copyright (c) 2022 MediaTek Inc. | ||||
| + * Author: Henry Yen <henry.yen@mediatek.com> | ||||
| + *         Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| + */ | ||||
| + | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/debugfs.h> | ||||
| +#include "mtk_eth_soc.h" | ||||
| + | ||||
| +#define MAX_PPPQ_PORT_NUM	6 | ||||
| + | ||||
| +static struct mtk_eth *_eth; | ||||
| + | ||||
| +static void mtk_qdma_qos_shaper_ebl(struct mtk_eth *eth, u32 id, u32 enable) | ||||
| +{ | ||||
| +	u32 val; | ||||
| + | ||||
| +	if (enable) { | ||||
| +		if (id < MAX_PPPQ_PORT_NUM) { | ||||
| +			val = MTK_QTX_SCH_MIN_RATE_EN | MTK_QTX_SCH_MAX_RATE_EN; | ||||
| +			val |=  FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN,  1) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP,  4) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 25) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP,  5) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 4); | ||||
| +		} else { | ||||
| +			val = MTK_QTX_SCH_MIN_RATE_EN; | ||||
| +			val |=  FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN,  1) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP,  3) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN,  0) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP,  0) | | ||||
| +		       		FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 4); | ||||
| +		} | ||||
| + | ||||
| +		writel(val, eth->base + MTK_QTX_SCH(id % MTK_QTX_PER_PAGE)); | ||||
| +	} else { | ||||
| +		writel(0, eth->base + MTK_QTX_SCH(id % MTK_QTX_PER_PAGE)); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static void mtk_qdma_qos_disable(struct mtk_eth *eth) | ||||
| +{ | ||||
| +	u32 id, val; | ||||
| + | ||||
| +	for (id = 0; id < MAX_PPPQ_PORT_NUM; id++) { | ||||
| +		mtk_qdma_qos_shaper_ebl(eth, id, 0); | ||||
| + | ||||
| +		writel(FIELD_PREP(MTK_QTX_CFG_HW_RESV_CNT_OFFSET, 4) | | ||||
| +		       FIELD_PREP(MTK_QTX_CFG_SW_RESV_CNT_OFFSET, 4), | ||||
| +		       eth->base + MTK_QTX_CFG(id % MTK_QTX_PER_PAGE)); | ||||
| +	} | ||||
| + | ||||
| +	val = (MTK_QDMA_TX_SCH_MAX_WFQ) | (MTK_QDMA_TX_SCH_MAX_WFQ << 16); | ||||
| +	for (id = 0; id < eth->soc->txrx.qdma_tx_sch; id += 2) { | ||||
| +		if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +			writel(val, eth->base + MTK_QDMA_TX_4SCH_BASE(id)); | ||||
| +		else | ||||
| +			writel(val, eth->base + MTK_QDMA_TX_2SCH_BASE); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static void mtk_qdma_qos_pppq_enable(struct mtk_eth *eth) | ||||
| +{ | ||||
| +	u32 id, val; | ||||
| + | ||||
| +	for (id = 0; id < 2 * MAX_PPPQ_PORT_NUM; id++) { | ||||
| +		mtk_qdma_qos_shaper_ebl(eth, id, 1); | ||||
| + | ||||
| +		writel(FIELD_PREP(MTK_QTX_CFG_HW_RESV_CNT_OFFSET, 4) | | ||||
| +		       FIELD_PREP(MTK_QTX_CFG_SW_RESV_CNT_OFFSET, 4), | ||||
| +		       eth->base + MTK_QTX_CFG(id % MTK_QTX_PER_PAGE)); | ||||
| +	} | ||||
| + | ||||
| +	val = (MTK_QDMA_TX_SCH_MAX_WFQ) | (MTK_QDMA_TX_SCH_MAX_WFQ << 16); | ||||
| +	for (id = 0; id < eth->soc->txrx.qdma_tx_sch; id+= 2) { | ||||
| +		if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +			writel(val, eth->base + MTK_QDMA_TX_4SCH_BASE(id)); | ||||
| +		else | ||||
| +			writel(val, eth->base + MTK_QDMA_TX_2SCH_BASE); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| + static ssize_t mtk_qmda_debugfs_write_qos(struct file *file, const char __user *buffer, | ||||
| +					   size_t count, loff_t *data) | ||||
| +{ | ||||
| +	struct seq_file *m = file->private_data; | ||||
| +	struct mtk_eth *eth = m->private; | ||||
| +	char buf[8]; | ||||
| +	int len = count; | ||||
| + | ||||
| +	if ((len > 8) || copy_from_user(buf, buffer, len)) | ||||
| +		return -EFAULT; | ||||
| + | ||||
| +	if (buf[0] == '0') { | ||||
| +		pr_info("HQoS is going to be disabled !\n"); | ||||
| +		eth->qos_toggle = 0; | ||||
| +		mtk_qdma_qos_disable(eth); | ||||
| +	} else if (buf[0] == '1') { | ||||
| +		pr_info("HQoS mode is going to be enabled !\n"); | ||||
| +		eth->qos_toggle = 1; | ||||
| +	} else if (buf[0] == '2') { | ||||
| +		pr_info("Per-port-per-queue mode is going to be enabled !\n"); | ||||
| +		pr_info("PPPQ use qid 0~11 (scheduler 0).\n"); | ||||
| +		eth->qos_toggle = 2; | ||||
| +		mtk_qdma_qos_pppq_enable(eth); | ||||
| +	} | ||||
| + | ||||
| +	return len; | ||||
| +} | ||||
| + | ||||
| +static int mtk_qmda_debugfs_read_qos(struct seq_file *m, void *private) | ||||
| +{ | ||||
| +	struct mtk_eth *eth = m->private; | ||||
| + | ||||
| +	if (eth->qos_toggle == 0) | ||||
| +		pr_info("HQoS is disabled now!\n"); | ||||
| +	else if (eth->qos_toggle == 1) | ||||
| +		pr_info("HQoS is enabled now!\n"); | ||||
| +	else if (eth->qos_toggle == 2) | ||||
| +		pr_info("Per-port-per-queue mode is enabled!\n"); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int mtk_qmda_debugfs_open_qos(struct inode *inode, struct file *file) | ||||
| +{ | ||||
| +	return single_open(file, mtk_qmda_debugfs_read_qos, | ||||
| +			   inode->i_private); | ||||
| +} | ||||
| + | ||||
| +static ssize_t mtk_qmda_debugfs_read_qos_sched(struct file *file, char __user *user_buf, | ||||
| +					       size_t count, loff_t *ppos) | ||||
| +{ | ||||
| +	struct mtk_eth *eth = _eth; | ||||
| +	long id = (long)file->private_data; | ||||
| +	char *buf; | ||||
| +	unsigned int len = 0, buf_len = 1500; | ||||
| +	int enable, scheduling, max_rate, exp, scheduler, i; | ||||
| +	ssize_t ret_cnt; | ||||
| +	u32 val; | ||||
| + | ||||
| +	buf = kzalloc(buf_len, GFP_KERNEL); | ||||
| +	if (!buf) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +		val = readl(eth->base + MTK_QDMA_TX_4SCH_BASE(id)); | ||||
| +	else | ||||
| +		val = readl(eth->base + MTK_QDMA_TX_2SCH_BASE); | ||||
| + | ||||
| +	if (id & 0x1) | ||||
| +		val >>= 16; | ||||
| + | ||||
| +	val &= MTK_QDMA_TX_SCH_MASK; | ||||
| +	enable     = FIELD_GET(MTK_QDMA_TX_SCH_RATE_EN, val); | ||||
| +	scheduling = FIELD_GET(MTK_QDMA_TX_SCH_MAX_WFQ, val); | ||||
| +	max_rate   = FIELD_GET(MTK_QDMA_TX_SCH_RATE_MAN, val); | ||||
| +	exp        = FIELD_GET(MTK_QDMA_TX_SCH_RATE_EXP, val); | ||||
| +	while (exp--) | ||||
| +		max_rate *= 10; | ||||
| + | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "EN\tScheduling\tMAX\tQueue#\n%d\t%s%16d\t", enable, | ||||
| +			 (scheduling == 1) ? "WRR" : "SP", max_rate); | ||||
| + | ||||
| +	for (i = 0; i < MTK_QDMA_TX_NUM; i++) { | ||||
| +		val = readl(eth->base + MTK_QDMA_PAGE) & ~MTK_QTX_CFG_PAGE; | ||||
| +		val |= FIELD_PREP(MTK_QTX_CFG_PAGE, i / MTK_QTX_PER_PAGE); | ||||
| +		writel(val, eth->base + MTK_QDMA_PAGE); | ||||
| + | ||||
| +		val = readl(eth->base + MTK_QTX_SCH(i % MTK_QTX_PER_PAGE)); | ||||
| +		if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +			scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL_V2, val); | ||||
| +		else | ||||
| +			scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL, val); | ||||
| +		if (id == scheduler) | ||||
| +			len += scnprintf(buf + len, buf_len - len, "%d  ", i); | ||||
| +	} | ||||
| + | ||||
| +	len += scnprintf(buf + len, buf_len - len, "\n"); | ||||
| +	if (len > buf_len) | ||||
| +		len = buf_len; | ||||
| + | ||||
| +	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||||
| + | ||||
| +	kfree(buf); | ||||
| +	return ret_cnt; | ||||
| +} | ||||
| + | ||||
| +static ssize_t mtk_qmda_debugfs_write_qos_sched(struct file *file, const char __user *buf, | ||||
| +						size_t length, loff_t *offset) | ||||
| +{ | ||||
| +	struct mtk_eth *eth = _eth; | ||||
| +	long id = (long)file->private_data; | ||||
| +	char line[64] = {0}, scheduling[32]; | ||||
| +	int enable, rate, exp = 0, shift = 0; | ||||
| +	size_t size; | ||||
| +	u32 sch, val = 0; | ||||
| + | ||||
| +	if (length >= sizeof(line)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	if (copy_from_user(line, buf, length)) | ||||
| +		return -EFAULT; | ||||
| + | ||||
| +	if (sscanf(line, "%d %s %d", &enable, scheduling, &rate) != 3) | ||||
| +		return -EFAULT; | ||||
| + | ||||
| +	while (rate > 127) { | ||||
| +		rate /= 10; | ||||
| +		exp++; | ||||
| +	} | ||||
| + | ||||
| +	line[length] = '\0'; | ||||
| + | ||||
| +	if (enable) | ||||
| +		val |= FIELD_PREP(MTK_QDMA_TX_SCH_RATE_EN, 1); | ||||
| +	if (strcmp(scheduling, "sp") != 0) | ||||
| +		val |= FIELD_PREP(MTK_QDMA_TX_SCH_MAX_WFQ, 1); | ||||
| +	val |= FIELD_PREP(MTK_QDMA_TX_SCH_RATE_MAN, rate); | ||||
| +	val |= FIELD_PREP(MTK_QDMA_TX_SCH_RATE_EXP, exp); | ||||
| + | ||||
| +	if (id & 0x1) | ||||
| +		shift = 16; | ||||
| + | ||||
| +	if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +		sch = readl(eth->base + MTK_QDMA_TX_4SCH_BASE(id)); | ||||
| +	else | ||||
| +		sch = readl(eth->base + MTK_QDMA_TX_2SCH_BASE); | ||||
| + | ||||
| +	sch &= ~(MTK_QDMA_TX_SCH_MASK << shift); | ||||
| +	sch |= val << shift; | ||||
| +	if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +		writel(sch, eth->base + MTK_QDMA_TX_4SCH_BASE(id)); | ||||
| +	else | ||||
| +		writel(sch, eth->base + MTK_QDMA_TX_2SCH_BASE); | ||||
| + | ||||
| +	size = strlen(line); | ||||
| +	*offset += size; | ||||
| + | ||||
| +	return length; | ||||
| +} | ||||
| + | ||||
| +static ssize_t mtk_qmda_debugfs_read_qos_queue(struct file *file, char __user *user_buf, | ||||
| +					       size_t count, loff_t *ppos) | ||||
| +{ | ||||
| +	struct mtk_eth *eth = _eth; | ||||
| +	long id = (long)file->private_data; | ||||
| +	char *buf; | ||||
| +	unsigned int len = 0, buf_len = 1500; | ||||
| +	int min_rate_en, min_rate, min_rate_exp; | ||||
| +	int max_rate_en, max_weight, max_rate, max_rate_exp; | ||||
| +	u32 qtx_sch, qtx_cfg, scheduler, val; | ||||
| +	ssize_t ret_cnt; | ||||
| + | ||||
| +	buf = kzalloc(buf_len, GFP_KERNEL); | ||||
| +	if (!buf) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	val = readl(eth->base + MTK_QDMA_PAGE) & ~MTK_QTX_CFG_PAGE; | ||||
| +	val |= FIELD_PREP(MTK_QTX_CFG_PAGE, id / MTK_QTX_PER_PAGE); | ||||
| +	writel(val, eth->base + MTK_QDMA_PAGE); | ||||
| + | ||||
| +	qtx_cfg = readl(eth->base + MTK_QTX_CFG(id % MTK_QTX_PER_PAGE)); | ||||
| +	qtx_sch = readl(eth->base + MTK_QTX_SCH(id % MTK_QTX_PER_PAGE)); | ||||
| +	if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +		scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL_V2, qtx_sch); | ||||
| +	else | ||||
| +		scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL, qtx_sch); | ||||
| + | ||||
| +	min_rate_en  = FIELD_GET(MTK_QTX_SCH_MIN_RATE_EN, qtx_sch); | ||||
| +	min_rate     = FIELD_GET(MTK_QTX_SCH_MIN_RATE_MAN, qtx_sch); | ||||
| +	min_rate_exp = FIELD_GET(MTK_QTX_SCH_MIN_RATE_EXP, qtx_sch); | ||||
| +	max_rate_en  = FIELD_GET(MTK_QTX_SCH_MAX_RATE_EN, qtx_sch); | ||||
| +	max_weight   = FIELD_GET(MTK_QTX_SCH_MAX_RATE_WEIGHT, qtx_sch); | ||||
| +	max_rate     = FIELD_GET(MTK_QTX_SCH_MAX_RATE_MAN, qtx_sch); | ||||
| +	max_rate_exp = FIELD_GET(MTK_QTX_SCH_MAX_RATE_EXP, qtx_sch); | ||||
| +	while (min_rate_exp--) | ||||
| +		min_rate *= 10; | ||||
| + | ||||
| +	while (max_rate_exp--) | ||||
| +		max_rate *= 10; | ||||
| + | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "scheduler: %d\nhw resv: %d\nsw resv: %d\n", scheduler, | ||||
| +			 (qtx_cfg >> 8) & 0xff, qtx_cfg & 0xff); | ||||
| + | ||||
| +	/* Switch to debug mode */ | ||||
| +	val = readl(eth->base + MTK_QTX_MIB_IF) & ~MTK_MIB_ON_QTX_CFG; | ||||
| +	val |= MTK_MIB_ON_QTX_CFG; | ||||
| +	writel(val, eth->base + MTK_QTX_MIB_IF); | ||||
| + | ||||
| +	val = readl(eth->base + MTK_QTX_MIB_IF) & ~MTK_VQTX_MIB_EN; | ||||
| +	val |= MTK_VQTX_MIB_EN; | ||||
| +	writel(val, eth->base + MTK_QTX_MIB_IF); | ||||
| + | ||||
| +	qtx_cfg = readl(eth->base + MTK_QTX_CFG(id % MTK_QTX_PER_PAGE)); | ||||
| +	qtx_sch = readl(eth->base + MTK_QTX_SCH(id % MTK_QTX_PER_PAGE)); | ||||
| + | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "packet count: %u\n", qtx_cfg); | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "packet drop: %u\n\n", qtx_sch); | ||||
| + | ||||
| +	/* Recover to normal mode */ | ||||
| +	val = readl(eth->base + MTK_QTX_MIB_IF); | ||||
| +	val &= ~MTK_MIB_ON_QTX_CFG; | ||||
| +	writel(val, eth->base + MTK_QTX_MIB_IF); | ||||
| + | ||||
| +	val = readl(eth->base + MTK_QTX_MIB_IF); | ||||
| +	val &= ~MTK_VQTX_MIB_EN; | ||||
| +	writel(val, eth->base + MTK_QTX_MIB_IF); | ||||
| + | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "      EN     RATE     WEIGHT\n"); | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "----------------------------\n"); | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "max%5d%9d%9d\n", max_rate_en, max_rate, max_weight); | ||||
| +	len += scnprintf(buf + len, buf_len - len, | ||||
| +			 "min%5d%9d        -\n", min_rate_en, min_rate); | ||||
| + | ||||
| +	if (len > buf_len) | ||||
| +		len = buf_len; | ||||
| + | ||||
| +	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||||
| + | ||||
| +	kfree(buf); | ||||
| + | ||||
| +	return ret_cnt; | ||||
| +} | ||||
| + | ||||
| +static ssize_t mtk_qmda_debugfs_write_qos_queue(struct file *file, const char __user *buf, | ||||
| +						size_t length, loff_t *offset) | ||||
| +{ | ||||
| +	struct mtk_eth *eth = _eth; | ||||
| +	long id = (long)file->private_data; | ||||
| +	char line[64] = {0}; | ||||
| +	int max_enable, max_rate, max_exp = 0; | ||||
| +	int min_enable, min_rate, min_exp = 0; | ||||
| +	int scheduler, weight, resv; | ||||
| +	size_t size; | ||||
| +	u32 val; | ||||
| + | ||||
| +	if (length >= sizeof(line)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	if (copy_from_user(line, buf, length)) | ||||
| +		return -EFAULT; | ||||
| + | ||||
| +	if (sscanf(line, "%d %d %d %d %d %d %d", &scheduler, &min_enable, &min_rate, | ||||
| +		   &max_enable, &max_rate, &weight, &resv) != 7) | ||||
| +		return -EFAULT; | ||||
| + | ||||
| +	line[length] = '\0'; | ||||
| + | ||||
| +	while (max_rate > 127) { | ||||
| +		max_rate /= 10; | ||||
| +		max_exp++; | ||||
| +	} | ||||
| + | ||||
| +	while (min_rate > 127) { | ||||
| +		min_rate /= 10; | ||||
| +		min_exp++; | ||||
| +	} | ||||
| + | ||||
| +	val = readl(eth->base + MTK_QDMA_PAGE) & ~MTK_QTX_CFG_PAGE; | ||||
| +	val |= FIELD_PREP(MTK_QTX_CFG_PAGE, id / MTK_QTX_PER_PAGE); | ||||
| +	writel(val, eth->base + MTK_QDMA_PAGE); | ||||
| + | ||||
| +	if (eth->soc->txrx.qdma_tx_sch == 4) | ||||
| +		val = FIELD_PREP(MTK_QTX_SCH_TX_SEL_V2, scheduler); | ||||
| +	else | ||||
| +		val = FIELD_PREP(MTK_QTX_SCH_TX_SEL, scheduler); | ||||
| +	if (min_enable) | ||||
| +		val |= MTK_QTX_SCH_MIN_RATE_EN; | ||||
| +	val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, min_rate); | ||||
| +	val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, min_exp); | ||||
| +	if (max_enable) | ||||
| +		val |= MTK_QTX_SCH_MAX_RATE_EN; | ||||
| +	val |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, weight); | ||||
| +	val |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, max_rate); | ||||
| +	val |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, max_exp); | ||||
| +	writel(val, eth->base + MTK_QTX_SCH(id % MTK_QTX_PER_PAGE)); | ||||
| + | ||||
| +	val = readl(eth->base + MTK_QTX_CFG(id % MTK_QTX_PER_PAGE)); | ||||
| +	val |= FIELD_PREP(MTK_QTX_CFG_HW_RESV_CNT_OFFSET, resv); | ||||
| +	val |= FIELD_PREP(MTK_QTX_CFG_SW_RESV_CNT_OFFSET, resv); | ||||
| +	writel(val, eth->base + MTK_QTX_CFG(id % MTK_QTX_PER_PAGE)); | ||||
| + | ||||
| +	size = strlen(line); | ||||
| +	*offset += size; | ||||
| + | ||||
| +	return length; | ||||
| +} | ||||
| + | ||||
| +int mtk_qdma_debugfs_init(struct mtk_eth *eth) | ||||
| +{ | ||||
| +	static const struct file_operations fops_qos = { | ||||
| +		.open = mtk_qmda_debugfs_open_qos, | ||||
| +		.read = seq_read, | ||||
| +		.llseek = seq_lseek, | ||||
| +		.write = mtk_qmda_debugfs_write_qos, | ||||
| +		.release = single_release, | ||||
| +	}; | ||||
| + | ||||
| +	static const struct file_operations fops_qos_sched = { | ||||
| +		.open = simple_open, | ||||
| +		.read = mtk_qmda_debugfs_read_qos_sched, | ||||
| +		.write = mtk_qmda_debugfs_write_qos_sched, | ||||
| +		.llseek = default_llseek, | ||||
| +	}; | ||||
| + | ||||
| +	static const struct file_operations fops_qos_queue = { | ||||
| +		.open = simple_open, | ||||
| +		.read = mtk_qmda_debugfs_read_qos_queue, | ||||
| +		.write = mtk_qmda_debugfs_write_qos_queue, | ||||
| +		.llseek = default_llseek, | ||||
| +	}; | ||||
| + | ||||
| +	struct dentry *root; | ||||
| +	long i; | ||||
| +	char name[16]; | ||||
| + | ||||
| +	_eth = eth; | ||||
| + | ||||
| +	root = debugfs_lookup("mtk_ppe", NULL); | ||||
| +	if (!root) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	debugfs_create_file("qos_toggle", S_IRUGO, root, eth, &fops_qos); | ||||
| + | ||||
| +	for (i = 0; i < eth->soc->txrx.qdma_tx_sch; i++) { | ||||
| +		snprintf(name, sizeof(name), "qdma_sch%ld", i); | ||||
| +		debugfs_create_file(name, S_IRUGO, root, (void *)i, | ||||
| +				    &fops_qos_sched); | ||||
| +	} | ||||
| + | ||||
| +	for (i = 0; i < MTK_QDMA_TX_NUM; i++) { | ||||
| +		snprintf(name, sizeof(name), "qdma_txq%ld", i); | ||||
| +		debugfs_create_file(name, S_IRUGO, root, (void *)i, | ||||
| +				    &fops_qos_queue); | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h | ||||
| index 59b8736..c4eb45c 100644 | ||||
| --- a/include/net/flow_offload.h | ||||
| +++ b/include/net/flow_offload.h | ||||
| @@ -365,6 +365,7 @@ struct flow_cls_offload { | ||||
|  	struct flow_cls_common_offload common; | ||||
|  	enum flow_cls_command command; | ||||
|  	unsigned long cookie; | ||||
| +	struct flow_offload *flow; | ||||
|  	struct flow_rule *rule; | ||||
|  	struct flow_stats stats; | ||||
|  	u32 classid; | ||||
| diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c | ||||
| index 50f2f2e..ba34572 100644 | ||||
| --- a/net/netfilter/nf_flow_table_offload.c | ||||
| +++ b/net/netfilter/nf_flow_table_offload.c | ||||
| @@ -810,11 +810,13 @@ static int nf_flow_offload_alloc(const struct flow_offload_work *offload, | ||||
|  } | ||||
|   | ||||
|  static void nf_flow_offload_init(struct flow_cls_offload *cls_flow, | ||||
| +				 struct flow_offload *flow, | ||||
|  				 __be16 proto, int priority, | ||||
|  				 enum flow_cls_command cmd, | ||||
|  				 const struct flow_offload_tuple *tuple, | ||||
|  				 struct netlink_ext_ack *extack) | ||||
|  { | ||||
| +	cls_flow->flow = flow; | ||||
|  	cls_flow->common.protocol = proto; | ||||
|  	cls_flow->common.prio = priority; | ||||
|  	cls_flow->common.extack = extack; | ||||
| @@ -836,7 +838,7 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable, | ||||
|  	__be16 proto = ETH_P_ALL; | ||||
|  	int err, i = 0; | ||||
|   | ||||
| -	nf_flow_offload_init(&cls_flow, proto, priority, cmd, | ||||
| +	nf_flow_offload_init(&cls_flow, flow, proto, priority, cmd, | ||||
|  			     &flow->tuplehash[dir].tuple, &extack); | ||||
|  	if (cmd == FLOW_CLS_REPLACE) | ||||
|  		cls_flow.rule = flow_rule->rule; | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,75 +0,0 @@ | ||||
| From f3112e335a7f95aeb3d834962de813baadc1f620 Mon Sep 17 00:00:00 2001 | ||||
| From: Evelyn Tsai <evelyn.tsai@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 11:11:41 +0800 | ||||
| Subject: [PATCH 13/24] flow-offload-ovs-support | ||||
|  | ||||
| --- | ||||
|  net/openvswitch/vport-internal_dev.c | 46 ++++++++++++++++++++++++++++ | ||||
|  1 file changed, 46 insertions(+) | ||||
|  | ||||
| diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c | ||||
| index 58a7b83..8475727 100644 | ||||
| --- a/net/openvswitch/vport-internal_dev.c | ||||
| +++ b/net/openvswitch/vport-internal_dev.c | ||||
| @@ -113,12 +113,58 @@ internal_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats) | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +static int internal_dev_fill_forward_path(struct net_device_path_ctx *ctx, struct net_device_path *path) | ||||
| +{ | ||||
| +	struct vport *vport; | ||||
| +	int i; | ||||
| +	struct table_instance *ti; | ||||
| +	struct datapath *dp; | ||||
| +	struct sw_flow_key *key; | ||||
| +	struct sw_flow_actions *sf_acts; | ||||
| +	struct nlattr *a; | ||||
| +	int rem; | ||||
| + | ||||
| +	vport = ovs_internal_dev_get_vport(ctx->dev); | ||||
| +	dp = vport->dp; | ||||
| +	ti = rcu_dereference_ovsl(dp->table.ti); | ||||
| + | ||||
| +	for (i = 0; i < ti->n_buckets; i++) { | ||||
| +		struct sw_flow *flow; | ||||
| +		struct hlist_head *head = &ti->buckets[i]; | ||||
| +		struct hlist_node *n; | ||||
| + | ||||
| +		hlist_for_each_entry_safe(flow, n, head, flow_table.node[ti->node_ver]) { | ||||
| +			key = &flow->key; | ||||
| + | ||||
| +			if((!memcmp(ctx->dev->dev_addr, key->eth.dst, ETH_ALEN)) && (!memcmp(ctx->daddr, key->eth.src, ETH_ALEN))){ | ||||
| +				sf_acts = rcu_dereference_ovsl(flow->sf_acts); | ||||
| +				for (a = sf_acts->actions, rem = sf_acts->actions_len; rem > 0; | ||||
| +			     	 a = nla_next(a, &rem)) { | ||||
| +				 	if(nla_type(a) == OVS_ACTION_ATTR_OUTPUT){ | ||||
| +						vport = ovs_vport_rcu(dp, key->phy.in_port); | ||||
| +						goto out; | ||||
| +			 		} | ||||
| +		     	} | ||||
| +			} | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +out: | ||||
| + | ||||
| +	path->type = DEV_PATH_BRIDGE; | ||||
| +	path->dev = ctx->dev; | ||||
| +	ctx->dev = vport->dev; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static const struct net_device_ops internal_dev_netdev_ops = { | ||||
|  	.ndo_open = internal_dev_open, | ||||
|  	.ndo_stop = internal_dev_stop, | ||||
|  	.ndo_start_xmit = internal_dev_xmit, | ||||
|  	.ndo_set_mac_address = eth_mac_addr, | ||||
|  	.ndo_get_stats64 = internal_get_stats, | ||||
| +	.ndo_fill_forward_path	 = internal_dev_fill_forward_path, | ||||
|  }; | ||||
|   | ||||
|  static struct rtnl_link_ops internal_dev_link_ops __read_mostly = { | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,335 +0,0 @@ | ||||
| From aaac91720ca1fd7679896286eac2b014e7150fca Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Mar 2024 16:35:07 +0800 | ||||
| Subject: [PATCH 15/24] ethernet-update-ppe-from-netsys2-to-netsys3 | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c   |  8 +++- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  7 ++-- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c       | 35 ++++++++++++++--- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.h       | 38 ++++++++++++++++--- | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   |  6 ++- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  7 ++++ | ||||
|  6 files changed, 82 insertions(+), 19 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index 952bf51..f477ff3 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -2447,7 +2447,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, | ||||
|  			skb_checksum_none_assert(skb); | ||||
|  		skb->protocol = eth_type_trans(skb, netdev); | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  		hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY_V2; | ||||
|  		reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON_V2, trxd.rxd5); | ||||
|  		if (hash != MTK_RXD5_FOE_ENTRY_V2) { | ||||
| @@ -5798,7 +5798,8 @@ static int mtk_probe(struct platform_device *pdev) | ||||
|   | ||||
|  		for (i = 0; i < eth->ppe_num; i++) { | ||||
|  			eth->ppe[i] = mtk_ppe_init(eth, | ||||
| -						   eth->base + MTK_ETH_PPE_BASE + i * 0x400, | ||||
| +						   eth->base + MTK_ETH_PPE_BASE + | ||||
| +						   (i == 2 ? 0xC00 : i * 0x400), | ||||
|  						   2, eth->soc->hash_way, i, | ||||
|  						   eth->soc->has_accounting); | ||||
|  			if (!eth->ppe[i]) { | ||||
| @@ -6065,6 +6066,9 @@ static const struct mtk_soc_data mt7988_data = { | ||||
|  	.required_clks = MT7988_CLKS_BITMAP, | ||||
|  	.required_pctl = false, | ||||
|  	.has_sram = true, | ||||
| +	.has_accounting = true, | ||||
| +	.hash_way = 4, | ||||
| +	.offload_version = 2, | ||||
|  	.rss_num = 4, | ||||
|  	.txrx = { | ||||
|  		.txd_size = sizeof(struct mtk_tx_dma_v2), | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index 58547af..9c46ac1 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -137,9 +137,10 @@ | ||||
|  #define MTK_GDMA_UCS_EN		BIT(20) | ||||
|  #define MTK_GDMA_STRP_CRC	BIT(16) | ||||
|  #define MTK_GDMA_TO_PDMA	0x0 | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  #define MTK_GDMA_TO_PPE0	0x3333 | ||||
|  #define MTK_GDMA_TO_PPE1	0x4444 | ||||
| +#define MTK_GDMA_TO_PPE2	0xcccc | ||||
|  #else | ||||
|  #define MTK_GDMA_TO_PPE0	0x4444 | ||||
|  #endif | ||||
| @@ -2018,14 +2019,14 @@ extern u32 dbg_show_level; | ||||
|   | ||||
|  static inline void mtk_set_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe, u32 val) | ||||
|  { | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  	foe->ib1 |= FIELD_PREP(MTK_FOE_IB1_UNBIND_SRC_PORT, val); | ||||
|  #endif | ||||
|  } | ||||
|   | ||||
|  static inline u32 mtk_get_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe) | ||||
|  { | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  	return FIELD_GET(MTK_FOE_IB1_UNBIND_SRC_PORT, foe->ib1); | ||||
|  #else | ||||
|  	return 0; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index 8388f65..184e29d 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -91,7 +91,7 @@ static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe) | ||||
|   | ||||
|  int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets) | ||||
|  { | ||||
| -	u32 val, cnt_r0, cnt_r1, cnt_r2; | ||||
| +	u32 val, cnt_r0, cnt_r1, cnt_r2, cnt_r3; | ||||
|  	u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high; | ||||
|   | ||||
|  	val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST; | ||||
| @@ -104,12 +104,23 @@ int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets) | ||||
|  	cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); | ||||
|  	cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2); | ||||
|   | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +	cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3); | ||||
| + | ||||
| +	byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0); | ||||
| +	byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH_V2, cnt_r1); | ||||
| +	pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_LOW_V2, cnt_r2); | ||||
| +	pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R3_PKT_CNT_HIGH, cnt_r3); | ||||
| +	*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low; | ||||
| +	*packets = ((u64)pkt_cnt_high << 32) | pkt_cnt_low; | ||||
| +#else | ||||
|  	byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0); | ||||
|  	byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1); | ||||
|  	pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1); | ||||
|  	pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2); | ||||
|  	*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low; | ||||
|  	*packets = (pkt_cnt_high << 16) | pkt_cnt_low; | ||||
| +#endif | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -211,7 +222,7 @@ int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, | ||||
|  	      MTK_FOE_IB1_BIND_CACHE; | ||||
|  	entry->ib1 = val; | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  	val = FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0xf) | | ||||
|  #else | ||||
|  	val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | | ||||
| @@ -403,7 +414,7 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|   | ||||
|  	*ib2 &= ~MTK_FOE_IB2_PORT_MG; | ||||
|  	*ib2 |= MTK_FOE_IB2_WDMA_WINFO; | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  	*ib2 |=  FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq); | ||||
|   | ||||
|  	l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) | | ||||
| @@ -422,11 +433,16 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|   | ||||
|  int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid) | ||||
|  { | ||||
| +	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); | ||||
|  	u32 *ib2 = mtk_foe_entry_ib2(entry); | ||||
|   | ||||
|  	*ib2 &= ~MTK_FOE_IB2_QID; | ||||
|  	*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, qid); | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +	l2->tport_id = 1; | ||||
| +#else | ||||
|  	*ib2 |= MTK_FOE_IB2_PSE_QOS; | ||||
| +#endif | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -922,13 +938,16 @@ int mtk_ppe_start(struct mtk_ppe *ppe) | ||||
|  	mtk_ppe_init_foe_table(ppe); | ||||
|  	ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys); | ||||
|   | ||||
| -	val = MTK_PPE_TB_CFG_ENTRY_80B | | ||||
| +	val = | ||||
| +#if !defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +	      MTK_PPE_TB_CFG_ENTRY_80B | | ||||
| +#endif | ||||
|  	      MTK_PPE_TB_CFG_AGE_NON_L4 | | ||||
|  	      MTK_PPE_TB_CFG_AGE_UNBIND | | ||||
|  	      MTK_PPE_TB_CFG_AGE_TCP | | ||||
|  	      MTK_PPE_TB_CFG_AGE_UDP | | ||||
|  	      MTK_PPE_TB_CFG_AGE_TCP_FIN | | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  	      MTK_PPE_TB_CFG_INFO_SEL | | ||||
|  #endif | ||||
|  	      FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS, | ||||
| @@ -988,12 +1007,16 @@ int mtk_ppe_start(struct mtk_ppe *ppe) | ||||
|  	      MTK_PPE_GLO_CFG_IP4_L4_CS_DROP | | ||||
|  	      MTK_PPE_GLO_CFG_IP4_CS_DROP | | ||||
|  	      MTK_PPE_GLO_CFG_MCAST_TB_EN | | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +	      MTK_PPE_GLO_CFG_CS0_PIPE_EN | | ||||
| +	      MTK_PPE_GLO_CFG_SRH_CACHE_FIRST_EN | | ||||
| +#endif | ||||
|  	      MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE; | ||||
|  	ppe_w32(ppe, MTK_PPE_GLO_CFG, val); | ||||
|   | ||||
|  	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0); | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777); | ||||
|  	ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f); | ||||
|  #endif | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| index 5ab864f..5529d64 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -8,7 +8,10 @@ | ||||
|  #include <linux/bitfield.h> | ||||
|  #include <linux/rhashtable.h> | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +#define MTK_MAX_PPE_NUM			3 | ||||
| +#define MTK_ETH_PPE_BASE		0x2000 | ||||
| +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
|  #define MTK_MAX_PPE_NUM			2 | ||||
|  #define MTK_ETH_PPE_BASE		0x2000 | ||||
|  #else | ||||
| @@ -22,7 +25,7 @@ | ||||
|  #define MTK_PPE_WAIT_TIMEOUT_US		1000000 | ||||
|   | ||||
|  #define MTK_FOE_IB1_UNBIND_TIMESTAMP	GENMASK(7, 0) | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  #define MTK_FOE_IB1_UNBIND_SRC_PORT	GENMASK(11, 8) | ||||
|  #define MTK_FOE_IB1_UNBIND_PACKETS	GENMASK(19, 12) | ||||
|  #define MTK_FOE_IB1_UNBIND_PREBIND	BIT(22) | ||||
| @@ -70,7 +73,7 @@ enum { | ||||
|  	MTK_PPE_PKT_TYPE_IPV6_6RD = 7, | ||||
|  }; | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  #define MTK_FOE_IB2_QID			GENMASK(6, 0) | ||||
|  #define MTK_FOE_IB2_PORT_MG		BIT(7) | ||||
|  #define MTK_FOE_IB2_PSE_QOS		BIT(8) | ||||
| @@ -98,7 +101,18 @@ enum { | ||||
|   | ||||
|  #define MTK_FOE_IB2_DSCP		GENMASK(31, 24) | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +#define MTK_FOE_WINFO_WCID		GENMASK(15, 0) | ||||
| +#define MTK_FOE_WINFO_BSS		GENMASK(23, 16) | ||||
| + | ||||
| +#define MTK_FOE_WINFO_PAO_USR_INFO	GENMASK(15, 0) | ||||
| +#define MTK_FOE_WINFO_PAO_TID		GENMASK(19, 16) | ||||
| +#define MTK_FOE_WINFO_PAO_IS_FIXEDRATE	BIT(20) | ||||
| +#define MTK_FOE_WINFO_PAO_IS_PRIOR	BIT(21) | ||||
| +#define MTK_FOE_WINFO_PAO_IS_SP		BIT(22) | ||||
| +#define MTK_FOE_WINFO_PAO_HF		BIT(23) | ||||
| +#define MTK_FOE_WINFO_PAO_AMSDU_EN	BIT(24) | ||||
| +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
|  #define MTK_FOE_WINFO_BSS		GENMASK(5, 0) | ||||
|  #define MTK_FOE_WINFO_WCID		GENMASK(15, 6) | ||||
|  #else | ||||
| @@ -128,7 +142,17 @@ struct mtk_foe_mac_info { | ||||
|  	u16 pppoe_id; | ||||
|  	u16 src_mac_lo; | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +	u16 minfo; | ||||
| +	u16 resv1; | ||||
| +	u32 winfo; | ||||
| +	u32 winfo_pao; | ||||
| +	u16 cdrt_id:8; | ||||
| +	u16 tops_entry:6; | ||||
| +	u16 resv3:2; | ||||
| +	u16 tport_id:4; | ||||
| +	u16 resv4:12; | ||||
| +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
|  	u16 minfo; | ||||
|  	u16 winfo; | ||||
|  #endif | ||||
| @@ -249,7 +273,9 @@ struct mtk_foe_entry { | ||||
|  		struct mtk_foe_ipv4_dslite dslite; | ||||
|  		struct mtk_foe_ipv6 ipv6; | ||||
|  		struct mtk_foe_ipv6_6rd ipv6_6rd; | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +		u32 data[31]; | ||||
| +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
|  		u32 data[23]; | ||||
|  #else | ||||
|  		u32 data[19]; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| index 3bc50a4..f0c63da 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -195,7 +195,7 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, | ||||
|  		mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss, | ||||
|  				       info.wcid); | ||||
|  		pse_port = PSE_PPE0_PORT; | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  		if (info.wdma_idx == 0) | ||||
|  			pse_port = PSE_WDMA0_PORT; | ||||
|  		else if (info.wdma_idx == 1) | ||||
| @@ -218,6 +218,8 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, | ||||
|  		pse_port = PSE_GDM1_PORT; | ||||
|  	else if (dev == eth->netdev[1]) | ||||
|  		pse_port = PSE_GDM2_PORT; | ||||
| +	else if (dev == eth->netdev[2]) | ||||
| +		pse_port = PSE_GDM3_PORT; | ||||
|  	else | ||||
|  		return -EOPNOTSUPP; | ||||
|   | ||||
| @@ -376,7 +378,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| -#if defined(CONFIG_MEDIATEK_NETSYS_V2) | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
|  	if (idev && idev->netdev_ops->ndo_fill_receive_path) { | ||||
|  		ctx.dev = idev; | ||||
|  		idev->netdev_ops->ndo_fill_receive_path(&ctx, &path); | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| index 8d3ebe1..55b9b0c 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h | ||||
| @@ -18,6 +18,8 @@ | ||||
|  #define MTK_PPE_GLO_CFG_UDP_LITE_EN		BIT(10) | ||||
|  #define MTK_PPE_GLO_CFG_UDP_LEN_DROP		BIT(11) | ||||
|  #define MTK_PPE_GLO_CFG_MCAST_ENTRIES		GNEMASK(13, 12) | ||||
| +#define MTK_PPE_GLO_CFG_CS0_PIPE_EN		BIT(29) | ||||
| +#define MTK_PPE_GLO_CFG_SRH_CACHE_FIRST_EN	BIT(30) | ||||
|  #define MTK_PPE_GLO_CFG_BUSY			BIT(31) | ||||
|   | ||||
|  #define MTK_PPE_FLOW_CFG			0x204 | ||||
| @@ -155,9 +157,14 @@ enum { | ||||
|  #define MTK_PPE_MIB_SER_R1			0x344 | ||||
|  #define MTK_PPE_MIB_SER_R1_PKT_CNT_LOW		GENMASK(31, 16) | ||||
|  #define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH	GENMASK(15, 0) | ||||
| +#define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH_V2	GENMASK(31, 0) | ||||
|   | ||||
|  #define MTK_PPE_MIB_SER_R2			0x348 | ||||
|  #define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH		GENMASK(23, 0) | ||||
| +#define MTK_PPE_MIB_SER_R2_PKT_CNT_LOW_V2	GENMASK(31, 0) | ||||
| + | ||||
| +#define MTK_PPE_MIB_SER_R3			0x34C | ||||
| +#define MTK_PPE_MIB_SER_R3_PKT_CNT_HIGH		GENMASK(31, 0) | ||||
|   | ||||
|  #define MTK_PPE_MIB_CACHE_CTL			0x350 | ||||
|  #define MTK_PPE_MIB_CACHE_CTL_EN		BIT(0) | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,165 +0,0 @@ | ||||
| From 743b10e8e2a17c904f27cf78d46aea64193fc41c Mon Sep 17 00:00:00 2001 | ||||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 11:16:18 +0800 | ||||
| Subject: [PATCH 16/24] mediatek-ethernet-add-wifi2wifi-offload-support | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  2 ++ | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   | 35 +++++++++++++------ | ||||
|  drivers/net/ethernet/mediatek/mtk_wed.c       | 13 +++++++ | ||||
|  include/linux/soc/mediatek/mtk_wed.h          |  5 +++ | ||||
|  4 files changed, 45 insertions(+), 10 deletions(-) | ||||
|  mode change 100755 => 100644 drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index f9dda59..88d2f46 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -2072,6 +2072,8 @@ void mtk_usxgmii_link_poll(struct work_struct *work); | ||||
|  int mtk_eth_offload_init(struct mtk_eth *eth, int id); | ||||
|  int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, | ||||
|  		     void *type_data); | ||||
| +int mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f, | ||||
| +			   struct mtk_eth *eth); | ||||
|  void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); | ||||
|  u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index); | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| old mode 100755 | ||||
| new mode 100644 | ||||
| index f0c63da..c1cce76 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -563,10 +563,20 @@ static int | ||||
|  mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) | ||||
|  { | ||||
|  	struct flow_cls_offload *cls = type_data; | ||||
| -	struct net_device *dev = cb_priv; | ||||
| -	struct mtk_mac *mac = netdev_priv(dev); | ||||
| -	struct mtk_eth *eth = mac->hw; | ||||
| -	int err; | ||||
| +	struct mtk_eth *eth = cb_priv; | ||||
| +	struct net_device *dev = NULL; | ||||
| +	int i, err; | ||||
| + | ||||
| +	for (i = 0; i < MTK_MAC_COUNT; i++) { | ||||
| +		if (!eth->netdev[i]) | ||||
| +			continue; | ||||
| + | ||||
| +		dev = eth->netdev[i]; | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	if (!dev) | ||||
| +		return -EOPNOTSUPP; | ||||
|   | ||||
|  	if (!tc_can_offload(dev)) | ||||
|  		return -EOPNOTSUPP; | ||||
| @@ -594,16 +604,21 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_pri | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -static int | ||||
| -mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f) | ||||
| +int | ||||
| +mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f, | ||||
| +		       struct mtk_eth *eth) | ||||
|  { | ||||
| -	struct mtk_mac *mac = netdev_priv(dev); | ||||
| -	struct mtk_eth *eth = mac->hw; | ||||
| +	struct mtk_mac *mac; | ||||
|  	static LIST_HEAD(block_cb_list); | ||||
|  	struct flow_block_cb *block_cb; | ||||
|  	flow_setup_cb_t *cb; | ||||
|  	int i, err = 0; | ||||
|   | ||||
| +	if (!eth) { | ||||
| +		mac = netdev_priv(dev); | ||||
| +		eth = mac->hw; | ||||
| +	} | ||||
| + | ||||
|  	for (i = 0; i < eth->ppe_num; i++) { | ||||
|  		if (!eth->ppe[i] || !eth->ppe[i]->foe_table) | ||||
|  			return -EOPNOTSUPP; | ||||
| @@ -622,7 +637,7 @@ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f) | ||||
|  			flow_block_cb_incref(block_cb); | ||||
|  			goto unlock; | ||||
|  		} | ||||
| -		block_cb = flow_block_cb_alloc(cb, dev, dev, NULL); | ||||
| +		block_cb = flow_block_cb_alloc(cb, dev, eth, NULL); | ||||
|  		if (IS_ERR(block_cb)) { | ||||
|  			err = PTR_ERR(block_cb); | ||||
|  			goto unlock; | ||||
| @@ -658,7 +673,7 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, | ||||
|  	switch (type) { | ||||
|  	case TC_SETUP_BLOCK: | ||||
|  	case TC_SETUP_FT: | ||||
| -		return mtk_eth_setup_tc_block(dev, type_data); | ||||
| +		return mtk_eth_setup_tc_block(dev, type_data, NULL); | ||||
|  	default: | ||||
|  		return -EOPNOTSUPP; | ||||
|  	} | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| index 5dd1182..68eedd3 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| @@ -1766,6 +1766,18 @@ void mtk_wed_flow_remove(int index) | ||||
|  	mutex_unlock(&hw_lock); | ||||
|  } | ||||
|   | ||||
| +static int mtk_wed_eth_setup_tc(struct mtk_wed_device *wed, struct net_device *dev, | ||||
| +				int type, void *type_data) | ||||
| +{ | ||||
| +	switch (type) { | ||||
| +	case TC_SETUP_BLOCK: | ||||
| +	case TC_SETUP_FT: | ||||
| +		return mtk_eth_setup_tc_block(dev, type_data, wed->hw->eth); | ||||
| +	default: | ||||
| +		return -EOPNOTSUPP; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, | ||||
|  		    void __iomem *wdma, phys_addr_t wdma_phy, | ||||
|  		    int index) | ||||
| @@ -1785,6 +1797,7 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, | ||||
|  		.irq_get = mtk_wed_irq_get, | ||||
|  		.irq_set_mask = mtk_wed_irq_set_mask, | ||||
|  		.detach = mtk_wed_detach, | ||||
| +		.setup_tc = mtk_wed_eth_setup_tc, | ||||
|  		.ppe_check = mtk_wed_ppe_check, | ||||
|  	}; | ||||
|  	struct device_node *eth_np = eth->dev->of_node; | ||||
| diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h | ||||
| index 6772ea8..470beb2 100644 | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h | ||||
| @@ -173,6 +173,8 @@ struct mtk_wed_ops { | ||||
|  	void (*detach)(struct mtk_wed_device *dev); | ||||
|  	void (*ppe_check)(struct mtk_wed_device *dev, struct sk_buff *skb, | ||||
|  			  u32 reason, u32 hash); | ||||
| +	int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev, | ||||
| +			int type, void *type_data); | ||||
|   | ||||
|  	void (*stop)(struct mtk_wed_device *dev); | ||||
|  	void (*start)(struct mtk_wed_device *dev, u32 irq_mask); | ||||
| @@ -241,6 +243,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev) | ||||
|  	(_dev)->ops->msg_update(_dev, _id, _msg, _len) | ||||
|  #define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev) | ||||
|  #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev) | ||||
| +#define mtk_wed_device_setup_tc(_dev, _ndev, _type, _data) \ | ||||
| +	(_dev)->ops->setup_tc(_dev, _ndev, _type, _data) | ||||
|  #else | ||||
|  static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -259,6 +263,7 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) | ||||
|  #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV | ||||
|  #define mtk_wed_device_stop(_dev) do {} while (0) | ||||
|  #define mtk_wed_device_dma_reset(_dev) do {} while (0) | ||||
| +#define mtk_wed_device_setup_tc(_dev, _ndev, _type, _data) do {} while (0) | ||||
|  #endif | ||||
|   | ||||
|  #endif | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,186 +0,0 @@ | ||||
| From 8c918e858df4b7cb12ea185acf23e83bae883cd2 Mon Sep 17 00:00:00 2001 | ||||
| From: Bc-bocun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 11:17:24 +0800 | ||||
| Subject: [PATCH 17/24] flow-offload-add-mtkhnat-dscp | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c       | 11 +++++++ | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.h       |  1 + | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   | 12 +++++++ | ||||
|  include/net/netfilter/nf_flow_table.h         |  2 ++ | ||||
|  net/netfilter/nf_flow_table_offload.c         |  7 +++- | ||||
|  net/netfilter/xt_FLOWOFFLOAD.c                | 32 +++++++++++++++++++ | ||||
|  6 files changed, 64 insertions(+), 1 deletion(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index 184e29d..0e9c0bd 100755 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -446,6 +446,17 @@ int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid) | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| + | ||||
| +int mtk_foe_entry_set_dscp(struct mtk_foe_entry *entry, int dscp) | ||||
| +{ | ||||
| +	u32 *ib2 = mtk_foe_entry_ib2(entry); | ||||
| + | ||||
| +	*ib2 &= ~MTK_FOE_IB2_DSCP; | ||||
| +	*ib2 |= FIELD_PREP(MTK_FOE_IB2_DSCP, dscp); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry) | ||||
|  { | ||||
|  	return !(entry->ib1 & MTK_FOE_IB1_STATIC) && | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| index 5529d64..2a8b6ef 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -430,6 +430,7 @@ int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid); | ||||
|  int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, | ||||
|  			   int bss, int wcid); | ||||
|  int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid); | ||||
| +int mtk_foe_entry_set_dscp(struct mtk_foe_entry *entry, int dscp); | ||||
|  bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data); | ||||
|  int mtk_foe_entry_set_sp(struct mtk_ppe *ppe, struct mtk_foe_entry *entry); | ||||
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| index c1cce76..95174b7 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -262,6 +262,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	int wed_index = -1; | ||||
|  	u16 addr_type = 0; | ||||
|  	u8 l4proto = 0; | ||||
| +	u8 dscp = 0; | ||||
|  	int err = 0; | ||||
|  	int i; | ||||
|   | ||||
| @@ -298,6 +299,15 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  		return -EOPNOTSUPP; | ||||
|  	} | ||||
|   | ||||
| +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) { | ||||
| +		struct flow_match_ip match; | ||||
| + | ||||
| +		flow_rule_match_ip(rule, &match); | ||||
| +		dscp = match.key->tos; | ||||
| +	} else { | ||||
| +		return -EOPNOTSUPP; | ||||
| +	} | ||||
| + | ||||
|  	switch (addr_type) { | ||||
|  	case 0: | ||||
|  		offload_type = MTK_PPE_PKT_TYPE_BRIDGE; | ||||
| @@ -471,6 +481,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  	if (data.pppoe.num == 1) | ||||
|  		mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid); | ||||
|   | ||||
| +	mtk_foe_entry_set_dscp(&foe, dscp); | ||||
| + | ||||
|  	mtk_foe_entry_set_sp(eth->ppe[ppe_index], &foe); | ||||
|   | ||||
|  	err = mtk_flow_set_output_device(eth, &foe, odev, f->flow->ct, data.eth.h_dest, | ||||
| diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h | ||||
| index 7374cb2..d5dd3fe 100644 | ||||
| --- a/include/net/netfilter/nf_flow_table.h | ||||
| +++ b/include/net/netfilter/nf_flow_table.h | ||||
| @@ -38,6 +38,7 @@ struct nf_flow_key { | ||||
|  	}; | ||||
|  	struct flow_dissector_key_tcp			tcp; | ||||
|  	struct flow_dissector_key_ports			tp; | ||||
| +	struct flow_dissector_key_ip			ip; | ||||
|  } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ | ||||
|   | ||||
|  struct nf_flow_match { | ||||
| @@ -147,6 +148,7 @@ struct flow_offload_tuple { | ||||
|  			u8		h_dest[ETH_ALEN]; | ||||
|  		} out; | ||||
|  	}; | ||||
| +	u8				tos; | ||||
|  }; | ||||
|   | ||||
|  struct flow_offload_tuple_rhash { | ||||
| diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c | ||||
| index ba34572..b8b2fa6 100644 | ||||
| --- a/net/netfilter/nf_flow_table_offload.c | ||||
| +++ b/net/netfilter/nf_flow_table_offload.c | ||||
| @@ -104,6 +104,7 @@ static int nf_flow_rule_match(struct nf_flow_match *match, | ||||
|  	NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); | ||||
|  	NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_TCP, tcp); | ||||
|  	NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_PORTS, tp); | ||||
| +	NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IP, ip); | ||||
|   | ||||
|  	if (other_dst && other_dst->lwtstate) { | ||||
|  		tun_info = lwt_tun_info(other_dst->lwtstate); | ||||
| @@ -183,10 +184,14 @@ static int nf_flow_rule_match(struct nf_flow_match *match, | ||||
|  	key->tp.dst = tuple->dst_port; | ||||
|  	mask->tp.dst = 0xffff; | ||||
|   | ||||
| +	key->ip.tos = tuple->tos; | ||||
| +	mask->ip.tos = 0xff; | ||||
| + | ||||
|  	match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_META) | | ||||
|  				      BIT(FLOW_DISSECTOR_KEY_CONTROL) | | ||||
|  				      BIT(FLOW_DISSECTOR_KEY_BASIC) | | ||||
| -				      BIT(FLOW_DISSECTOR_KEY_PORTS); | ||||
| +				      BIT(FLOW_DISSECTOR_KEY_PORTS) | | ||||
| +				      BIT(FLOW_DISSECTOR_KEY_IP); | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| diff --git a/net/netfilter/xt_FLOWOFFLOAD.c b/net/netfilter/xt_FLOWOFFLOAD.c | ||||
| index aae37f5..5364a32 100644 | ||||
| --- a/net/netfilter/xt_FLOWOFFLOAD.c | ||||
| +++ b/net/netfilter/xt_FLOWOFFLOAD.c | ||||
| @@ -49,6 +49,35 @@ static DEFINE_SPINLOCK(hooks_lock); | ||||
|   | ||||
|  struct xt_flowoffload_table flowtable[2]; | ||||
|   | ||||
| +static int | ||||
| +xt_flowoffload_dscp_init(struct sk_buff *skb, struct flow_offload *flow, | ||||
| +			 enum ip_conntrack_dir dir) | ||||
| +{ | ||||
| +	const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; | ||||
| +	struct iphdr *iph; | ||||
| +	struct ipv6hdr *ip6h; | ||||
| +	u32 offset = 0; | ||||
| +	u8 tos = 0; | ||||
| + | ||||
| +	switch (flow_tuple->l3proto) { | ||||
| +	case NFPROTO_IPV4: | ||||
| +		iph = (struct iphdr *)(skb_network_header(skb) + offset); | ||||
| +		tos = iph->tos; | ||||
| +		break; | ||||
| +	case NFPROTO_IPV6: | ||||
| +		ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset); | ||||
| +		tos = ipv6_get_dsfield(ip6h); | ||||
| +		break; | ||||
| +	default: | ||||
| +		return -1; | ||||
| +	}; | ||||
| + | ||||
| +	flow->tuplehash[dir].tuple.tos = tos; | ||||
| +	flow->tuplehash[!dir].tuple.tos = tos; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static unsigned int | ||||
|  xt_flowoffload_net_hook(void *priv, struct sk_buff *skb, | ||||
|  			const struct nf_hook_state *state) | ||||
| @@ -623,6 +652,9 @@ flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||||
|  	if (flow_offload_route_init(flow, &route) < 0) | ||||
|  		goto err_flow_add; | ||||
|   | ||||
| +	if (xt_flowoffload_dscp_init(skb, flow, dir) < 0) | ||||
| +		goto err_flow_add; | ||||
| + | ||||
|  	if (tcph) { | ||||
|  		ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; | ||||
|  		ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,355 +0,0 @@ | ||||
| From 122cb2a2ae7ececd20412083b5bb9bfd6f9c8d26 Mon Sep 17 00:00:00 2001 | ||||
| From: Bc-bocun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 13:14:08 +0800 | ||||
| Subject: [PATCH 18/24] flow-offload-add-mtkhnat-netlink | ||||
|  | ||||
| --- | ||||
|  include/net/netfilter/nf_flow_table.h    |   1 + | ||||
|  include/uapi/linux/netfilter/nfnetlink.h |   3 +- | ||||
|  net/netfilter/Kconfig                    |   9 + | ||||
|  net/netfilter/Makefile                   |   1 + | ||||
|  net/netfilter/nf_flow_table_core.c       |  23 +++ | ||||
|  net/netfilter/nf_flow_table_netlink.c    | 239 +++++++++++++++++++++++ | ||||
|  6 files changed, 275 insertions(+), 1 deletion(-) | ||||
|  create mode 100644 net/netfilter/nf_flow_table_netlink.c | ||||
|  | ||||
| diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h | ||||
| index d5dd3fe..f2bce73 100644 | ||||
| --- a/include/net/netfilter/nf_flow_table.h | ||||
| +++ b/include/net/netfilter/nf_flow_table.h | ||||
| @@ -279,6 +279,7 @@ int nf_flow_table_init(struct nf_flowtable *flow_table); | ||||
|  void nf_flow_table_free(struct nf_flowtable *flow_table); | ||||
|   | ||||
|  void flow_offload_teardown(struct flow_offload *flow); | ||||
| +void flow_offload_teardown_by_tuple(struct flow_offload_tuple *tuple); | ||||
|   | ||||
|  int nf_flow_table_iterate(struct nf_flowtable *flow_table, | ||||
|  			  void (*iter)(struct flow_offload *flow, void *data), | ||||
| diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h | ||||
| index 5bc960f..603d9c0 100644 | ||||
| --- a/include/uapi/linux/netfilter/nfnetlink.h | ||||
| +++ b/include/uapi/linux/netfilter/nfnetlink.h | ||||
| @@ -60,7 +60,8 @@ struct nfgenmsg { | ||||
|  #define NFNL_SUBSYS_CTHELPER		9 | ||||
|  #define NFNL_SUBSYS_NFTABLES		10 | ||||
|  #define NFNL_SUBSYS_NFT_COMPAT		11 | ||||
| -#define NFNL_SUBSYS_COUNT		12 | ||||
| +#define NFNL_SUBSYS_FLOWTABLE		12 | ||||
| +#define NFNL_SUBSYS_COUNT		13 | ||||
|   | ||||
|  /* Reserved control nfnetlink messages */ | ||||
|  #define NFNL_MSG_BATCH_BEGIN		NLMSG_MIN_TYPE | ||||
| diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig | ||||
| index 5d690ab..8ec87aa 100644 | ||||
| --- a/net/netfilter/Kconfig | ||||
| +++ b/net/netfilter/Kconfig | ||||
| @@ -708,6 +708,15 @@ config NF_FLOW_TABLE | ||||
|   | ||||
|  	  To compile it as a module, choose M here. | ||||
|   | ||||
| +config NF_FLOW_TABLE_NETLINK | ||||
| +	tristate "Netfilter flow table netlink module" | ||||
| +	depends on NETFILTER_INGRESS | ||||
| +	depends on NF_CONNTRACK | ||||
| +	help | ||||
| +	  This option adds the flow table core infrastructure. | ||||
| + | ||||
| +	  To compile it as a module, choose M here. | ||||
| + | ||||
|  config NETFILTER_XTABLES | ||||
|  	tristate "Netfilter Xtables support (required for ip_tables)" | ||||
|  	default m if NETFILTER_ADVANCED=n | ||||
| diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile | ||||
| index d93a121..fa6ffb1 100644 | ||||
| --- a/net/netfilter/Makefile | ||||
| +++ b/net/netfilter/Makefile | ||||
| @@ -124,6 +124,7 @@ nf_flow_table-objs		:= nf_flow_table_core.o nf_flow_table_ip.o \ | ||||
|  				   nf_flow_table_offload.o | ||||
|   | ||||
|  obj-$(CONFIG_NF_FLOW_TABLE_INET) += nf_flow_table_inet.o | ||||
| +obj-$(CONFIG_NF_FLOW_TABLE_NETLINK)	+= nf_flow_table_netlink.o | ||||
|   | ||||
|  # generic X tables | ||||
|  obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o | ||||
| diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c | ||||
| index c3054af..fb06755 100644 | ||||
| --- a/net/netfilter/nf_flow_table_core.c | ||||
| +++ b/net/netfilter/nf_flow_table_core.c | ||||
| @@ -373,6 +373,29 @@ void flow_offload_teardown(struct flow_offload *flow) | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(flow_offload_teardown); | ||||
|   | ||||
| +void flow_offload_teardown_by_tuple(struct flow_offload_tuple *tuple) | ||||
| +{ | ||||
| +	struct net_device *netdev; | ||||
| +	struct nf_flowtable *flowtable; | ||||
| +	struct flow_offload_tuple_rhash *tuplehash; | ||||
| +	struct flow_offload *flow; | ||||
| +	int dir; | ||||
| + | ||||
| +	list_for_each_entry(flowtable, &flowtables, list) { | ||||
| +		for_each_netdev(&init_net, netdev) { | ||||
| +			tuple->iifidx = netdev->ifindex; | ||||
| +			tuplehash = flow_offload_lookup(flowtable, tuple); | ||||
| +			if (!tuplehash) | ||||
| +				continue; | ||||
| + | ||||
| +			dir = tuplehash->tuple.dir; | ||||
| +			flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); | ||||
| +			flow_offload_teardown(flow); | ||||
| +		} | ||||
| +	}; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(flow_offload_teardown_by_tuple); | ||||
| + | ||||
|  struct flow_offload_tuple_rhash * | ||||
|  flow_offload_lookup(struct nf_flowtable *flow_table, | ||||
|  		    struct flow_offload_tuple *tuple) | ||||
| diff --git a/net/netfilter/nf_flow_table_netlink.c b/net/netfilter/nf_flow_table_netlink.c | ||||
| new file mode 100644 | ||||
| index 0000000..f05f29e | ||||
| --- /dev/null | ||||
| +++ b/net/netfilter/nf_flow_table_netlink.c | ||||
| @@ -0,0 +1,239 @@ | ||||
| +#include <linux/types.h> | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <linux/netlink.h> | ||||
| +#include <net/netlink.h> | ||||
| +#include <net/ip.h> | ||||
| +#include <linux/netfilter/nfnetlink.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
| + | ||||
| +enum ft_netlink_msg_types { | ||||
| +	FT_MSG_DEL, | ||||
| +	FT_MSG_ADD, | ||||
| +	FT_MSG_FLUSH, | ||||
| +	FT_MSG_MAX | ||||
| +}; | ||||
| + | ||||
| +enum ftattr_type { | ||||
| +	FTA_UNSPEC, | ||||
| +	FTA_TUPLE, | ||||
| +	__FTA_MAX | ||||
| +}; | ||||
| +#define FTA_MAX (__FTA_MAX - 1) | ||||
| + | ||||
| +enum ftattr_tuple { | ||||
| +	FTA_TUPLE_UNSPEC, | ||||
| +	FTA_TUPLE_IP, | ||||
| +	FTA_TUPLE_PROTO, | ||||
| +	FTA_TUPLE_ZONE, | ||||
| +	__FTA_TUPLE_MAX | ||||
| +}; | ||||
| +#define FTA_TUPLE_MAX (__FTA_TUPLE_MAX - 1) | ||||
| + | ||||
| +enum ftattr_ip { | ||||
| +	FTA_IP_UNSPEC, | ||||
| +	FTA_IP_V4_SRC, | ||||
| +	FTA_IP_V4_DST, | ||||
| +	__FTA_IP_MAX | ||||
| +}; | ||||
| +#define FTA_IP_MAX (__FTA_IP_MAX - 1) | ||||
| + | ||||
| +enum ftattr_l4proto { | ||||
| +	FTA_PROTO_UNSPEC, | ||||
| +	FTA_PROTO_NUM, | ||||
| +	FTA_PROTO_SPORT, | ||||
| +	FTA_PROTO_DPORT, | ||||
| +	__FTA_PROTO_MAX | ||||
| +}; | ||||
| +#define FTA_PROTO_MAX (__FTA_PROTO_MAX - 1) | ||||
| + | ||||
| +static const struct nla_policy tuple_nla_policy[FTA_TUPLE_MAX + 1] = { | ||||
| +	[FTA_TUPLE_IP]		= { .type = NLA_NESTED }, | ||||
| +	[FTA_TUPLE_PROTO]	= { .type = NLA_NESTED }, | ||||
| +	[FTA_TUPLE_ZONE]	= { .type = NLA_U16 }, | ||||
| +}; | ||||
| + | ||||
| +static const struct nla_policy ip_nla_policy[FTA_IP_MAX + 1] = { | ||||
| +	[FTA_IP_V4_SRC]		= { .type = NLA_U32 }, | ||||
| +	[FTA_IP_V4_DST]		= { .type = NLA_U32 }, | ||||
| +}; | ||||
| + | ||||
| +static const struct nla_policy l4proto_nla_policy[FTA_PROTO_MAX + 1] = { | ||||
| +	[FTA_PROTO_NUM]		= { .type = NLA_U8 }, | ||||
| +	[FTA_PROTO_SPORT]	= {.type = NLA_U16}, | ||||
| +	[FTA_PROTO_DPORT]	= {.type = NLA_U16}, | ||||
| +}; | ||||
| + | ||||
| +static inline int ftnetlink_parse_tuple_ip(struct nlattr *attr, | ||||
| +					   struct flow_offload_tuple *tuple) | ||||
| +{ | ||||
| +	struct nlattr *tb[FTA_IP_MAX+1]; | ||||
| +	int err; | ||||
| + | ||||
| +	err = nla_parse_nested_deprecated(tb, FTA_IP_MAX, attr, ip_nla_policy, NULL); | ||||
| + | ||||
| +	if (err < 0) | ||||
| +		return err; | ||||
| + | ||||
| +	switch (tuple->l3proto) { | ||||
| +	case NFPROTO_IPV4: | ||||
| +		if (!tb[FTA_IP_V4_SRC] || !tb[FTA_IP_V4_DST]) | ||||
| +			return -EINVAL; | ||||
| + | ||||
| +		tuple->src_v4.s_addr = nla_get_in_addr(tb[FTA_IP_V4_SRC]); | ||||
| +		tuple->dst_v4.s_addr = nla_get_in_addr(tb[FTA_IP_V4_DST]); | ||||
| +	} | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static inline int ftnetlink_parse_tuple_proto(struct nlattr *attr, | ||||
| +					      struct flow_offload_tuple *tuple) | ||||
| +{ | ||||
| +	struct nlattr *tb[FTA_PROTO_MAX+1]; | ||||
| +	int err; | ||||
| + | ||||
| +	err = nla_parse_nested_deprecated(tb, FTA_PROTO_MAX, attr, l4proto_nla_policy, NULL); | ||||
| + | ||||
| +	if(err < 0) | ||||
| +		return err; | ||||
| + | ||||
| +	if (!tb[FTA_PROTO_NUM] || !tb[FTA_PROTO_SPORT] || !tb[FTA_PROTO_DPORT]) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	tuple->l4proto = nla_get_u8(tb[FTA_PROTO_NUM]); | ||||
| +	tuple->src_port = nla_get_u16(tb[FTA_PROTO_SPORT]); | ||||
| +	tuple->dst_port = nla_get_u16(tb[FTA_PROTO_DPORT]); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int ftnetlink_parse_tuple(const struct nlattr * const cda[], | ||||
| +				 struct flow_offload_tuple *tuple, | ||||
| +				 int attrtype, int l3proto) | ||||
| +{ | ||||
| +	struct nlattr *tb[FTA_TUPLE_MAX+1]; | ||||
| +	int err; | ||||
| + | ||||
| +	memset(tuple, 0, sizeof(*tuple)); | ||||
| + | ||||
| +	err = nla_parse_nested_deprecated(tb, FTA_TUPLE_MAX, cda[attrtype], tuple_nla_policy, NULL); | ||||
| +	if (err < 0) | ||||
| +		return err; | ||||
| + | ||||
| +	if (!tb[FTA_TUPLE_IP]) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	/* parse IP */ | ||||
| +	tuple->l3proto = l3proto; | ||||
| +	err = ftnetlink_parse_tuple_ip(tb[FTA_TUPLE_IP], tuple); | ||||
| +	if (err < 0) | ||||
| +		return err; | ||||
| + | ||||
| +	/* parse proto */ | ||||
| +	if (!tb[FTA_TUPLE_PROTO]) | ||||
| +		return -EINVAL; | ||||
| +	err = ftnetlink_parse_tuple_proto(tb[FTA_TUPLE_PROTO], tuple); | ||||
| + | ||||
| +	if (err >= 0) | ||||
| +		printk("tuple info:sip=%pI4,dip=%pI4 proto=%d " | ||||
| +		       "sport=%d dport=%d\n", | ||||
| +		       &tuple->src_v4, &tuple->dst_v4, tuple->l4proto, | ||||
| +		       ntohs(tuple->src_port), ntohs(tuple->dst_port)); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static int ftnetlink_del_nf_flow(struct net *net, struct sock *ftnl, struct sk_buff *skb, | ||||
| +				 const struct nlmsghdr *nlh, | ||||
| +				 const struct nlattr * const cda[], | ||||
| +				 struct netlink_ext_ack *extack) | ||||
| +{ | ||||
| +	struct net_device *dev = skb->dev; | ||||
| +	struct flow_offload_tuple tuple; | ||||
| +	int err = -1; | ||||
| +	struct nfgenmsg *nfmsg = nlmsg_data(nlh); | ||||
| +	u_int8_t u3 = nfmsg->nfgen_family; | ||||
| + | ||||
| +	/* parse tuple */ | ||||
| +	if(!cda[FTA_TUPLE]) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	err = ftnetlink_parse_tuple(cda, &tuple, FTA_TUPLE, u3); | ||||
| +	if (err < 0) | ||||
| +		return err; | ||||
| + | ||||
| +	/* teardown the flow */ | ||||
| +	flow_offload_teardown_by_tuple(&tuple); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int ftnetlink_add_nf_flow(struct net *net, struct sock *ftnl, struct sk_buff *skb, | ||||
| +				 const struct nlmsghdr *nlh, | ||||
| +				 const struct nlattr * const cda[], | ||||
| +				 struct netlink_ext_ack *extack) | ||||
| +{ | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int ftnetlink_flush_table(struct net *net, struct sock *ftnl, struct sk_buff *skb, | ||||
| +				 const struct nlmsghdr *nlh, | ||||
| +				 const struct nlattr * const cda[], | ||||
| +				 struct netlink_ext_ack *extack) | ||||
| +{ | ||||
| +	struct net_device *dev = skb->dev; | ||||
| + | ||||
| +	nf_flow_table_cleanup(dev); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static const struct nla_policy ft_nla_policy[FTA_MAX + 1] = { | ||||
| +	[FTA_TUPLE] = { .type = NLA_NESTED }, | ||||
| +}; | ||||
| + | ||||
| +static const struct nfnl_callback flow_table_cb[FT_MSG_MAX] = { | ||||
| +	[FT_MSG_DEL] = { | ||||
| +		.call = ftnetlink_del_nf_flow, | ||||
| +		.attr_count = FTA_MAX, | ||||
| +		.policy = ft_nla_policy | ||||
| +	}, | ||||
| +	[FT_MSG_ADD] = { | ||||
| +		.call = ftnetlink_add_nf_flow, | ||||
| +		.attr_count = FTA_MAX, | ||||
| +		.policy = ft_nla_policy | ||||
| +	}, | ||||
| +	[FT_MSG_FLUSH] = { | ||||
| +		.call = ftnetlink_flush_table, | ||||
| +		.attr_count = FTA_MAX, | ||||
| +		.policy = ft_nla_policy | ||||
| +	}, | ||||
| +}; | ||||
| + | ||||
| +static const struct nfnetlink_subsystem ftnl_subsys = { | ||||
| +	.name = "flowtable", | ||||
| +	.subsys_id = NFNL_SUBSYS_FLOWTABLE, | ||||
| +	.cb_count = FT_MSG_MAX, | ||||
| +	.cb = flow_table_cb, | ||||
| +}; | ||||
| + | ||||
| +static int __init ftnetlink_init(void) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = nfnetlink_subsys_register(&ftnl_subsys); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static void ftnetlink_exit(void) | ||||
| +{ | ||||
| +	nfnetlink_subsys_unregister(&ftnl_subsys); | ||||
| +} | ||||
| + | ||||
| +MODULE_LICENSE("GPL"); | ||||
| +module_init(ftnetlink_init); | ||||
| +module_exit(ftnetlink_exit); | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,697 +0,0 @@ | ||||
| From 67a20e07982e9c43d299679f75fd81638271fc63 Mon Sep 17 00:00:00 2001 | ||||
| From: mtk27745 <rex.lu@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 13:22:44 +0800 | ||||
| Subject: [PATCH 2/6] mtk wed add wed3 ser support | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed.c      | 339 ++++++++++++++++--- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed_regs.h |  68 +++- | ||||
|  include/linux/soc/mediatek/mtk_wed.h         |   8 +- | ||||
|  3 files changed, 367 insertions(+), 48 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| index 4b32a82..02c156a 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| @@ -110,24 +110,88 @@ mtk_wdma_read_reset(struct mtk_wed_device *dev) | ||||
|  	return wdma_r32(dev, MTK_WDMA_GLO_CFG); | ||||
|  } | ||||
|   | ||||
| -static u32 | ||||
| -mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
| +static void | ||||
| +mtk_wdma_v3_rx_reset(struct mtk_wed_device *dev) | ||||
|  { | ||||
| -	if (wed_r32(dev, reg) & mask) | ||||
| -		return true; | ||||
| - | ||||
| -	return false; | ||||
| -} | ||||
| +	u32 status; | ||||
|   | ||||
| -static int | ||||
| -mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
| -{ | ||||
| -	int sleep = 1000; | ||||
| -	int timeout = 100 * sleep; | ||||
| -	u32 val; | ||||
| +	if (!mtk_wed_is_v3_or_greater(dev->hw)) | ||||
| +		return; | ||||
|   | ||||
| -	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, | ||||
| -				 timeout, false, dev, reg, mask); | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n"); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n"); | ||||
| + | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n"); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n"); | ||||
| + | ||||
| +	/* prefetch FIFO */ | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_RX_FIFO_CFG, | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR | | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_RX_FIFO_CFG, | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR | | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR); | ||||
| + | ||||
| +	/* core FIFO */ | ||||
| +	wdma_w32(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR); | ||||
| + | ||||
| +	/* writeback FIFO */ | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); | ||||
| + | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); | ||||
| + | ||||
| +	/* prefetch ring status */ | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR); | ||||
| + | ||||
| +	/* writeback ring status */ | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR); | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| @@ -142,6 +206,7 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev) | ||||
|  	if (ret) | ||||
|  		dev_err(dev->hw->dev, "rx reset failed \n"); | ||||
|   | ||||
| +	mtk_wdma_v3_rx_reset(dev); | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
|   | ||||
| @@ -156,6 +221,101 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev) | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static u32 | ||||
| +mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
| +{ | ||||
| +	return !!(wed_r32(dev, reg) & mask); | ||||
| +} | ||||
| + | ||||
| +static int | ||||
| +mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
| +{ | ||||
| +	int sleep = 15000; | ||||
| +	int timeout = 100 * sleep; | ||||
| +	u32 val; | ||||
| + | ||||
| +	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, | ||||
| +				 timeout, false, dev, reg, mask); | ||||
| +} | ||||
| + | ||||
| +static void | ||||
| +mtk_wdma_v3_tx_reset(struct mtk_wed_device *dev) | ||||
| +{ | ||||
| +	u32 status; | ||||
| + | ||||
| +	if (!mtk_wed_is_v3_or_greater(dev->hw)) | ||||
| +		return; | ||||
| + | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n"); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n"); | ||||
| + | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n"); | ||||
| + | ||||
| +	if (read_poll_timeout(wdma_r32, status, | ||||
| +			      !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG)) | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n"); | ||||
| + | ||||
| +	/* prefetch FIFO */ | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_TX_FIFO_CFG, | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR | | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_TX_FIFO_CFG, | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR | | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR); | ||||
| + | ||||
| +	/* core FIFO */ | ||||
| +	wdma_w32(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR | | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR); | ||||
| + | ||||
| +	/* writeback FIFO */ | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); | ||||
| + | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); | ||||
| + | ||||
| +	/* prefetch ring status */ | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR); | ||||
| + | ||||
| +	/* writeback ring status */ | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR); | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR); | ||||
| +} | ||||
| + | ||||
|  static void | ||||
|  mtk_wdma_tx_reset(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -167,6 +327,7 @@ mtk_wdma_tx_reset(struct mtk_wed_device *dev) | ||||
|  			       !(status & mask), 0, 10000)) | ||||
|  		dev_err(dev->hw->dev, "tx reset failed \n"); | ||||
|   | ||||
| +	mtk_wdma_v3_tx_reset(dev); | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
|   | ||||
| @@ -1389,25 +1550,6 @@ mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx) | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -static u32 | ||||
| -mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
| -{ | ||||
| -	if (wed_r32(dev, reg) & mask) | ||||
| -		return true; | ||||
| - | ||||
| -	return false; | ||||
| -} | ||||
| - | ||||
| -static int | ||||
| -mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) | ||||
| -{ | ||||
| -	int sleep = 1000; | ||||
| -	int timeout = 100 * sleep; | ||||
| -	u32 val; | ||||
| - | ||||
| -	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, | ||||
| -				 timeout, false, dev, reg, mask); | ||||
| -} | ||||
|   | ||||
|  static int | ||||
|  mtk_wed_rx_reset(struct mtk_wed_device *dev) | ||||
| @@ -1423,13 +1565,32 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev) | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| +	if (dev->wlan.hw_rro) { | ||||
| +		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN); | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_HW_STS, | ||||
| +				  MTK_WED_RX_IND_CMD_BUSY); | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_RRO_RX_TO_PG); | ||||
| +	} | ||||
| + | ||||
|  	wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN); | ||||
|  	ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, | ||||
|  				MTK_WED_WPDMA_RX_D_RX_DRV_BUSY); | ||||
| +	if (!ret && mtk_wed_is_v3_or_greater(dev->hw)) | ||||
| +		ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, | ||||
| +					 MTK_WED_WPDMA_RX_D_PREF_BUSY); | ||||
|  	if (ret) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV); | ||||
|  	} else { | ||||
| +		if (mtk_wed_is_v3_or_greater(dev->hw)) { | ||||
| +			/*1.a. Disable Prefetch HW*/ | ||||
| +			wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, MTK_WED_WPDMA_RX_D_PREF_EN); | ||||
| +			mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, | ||||
| +					  MTK_WED_WPDMA_RX_D_PREF_BUSY); | ||||
| +			wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, | ||||
| +				MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL); | ||||
| +		} | ||||
| + | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, | ||||
|  			MTK_WED_WPDMA_RX_D_RST_CRX_IDX | | ||||
|  			MTK_WED_WPDMA_RX_D_RST_DRV_IDX); | ||||
| @@ -1457,15 +1618,36 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev) | ||||
|  		wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| +	if (dev->wlan.hw_rro) { | ||||
| +		/* Disable RRO MSDU Page Drv */ | ||||
| +		wed_clr(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_EN); | ||||
| + | ||||
| +		/* Disable RRO Data Drv */ | ||||
| +		wed_clr(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN); | ||||
| + | ||||
| +		/* RRO MSDU Page Drv Reset */ | ||||
| +		wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_CLR); | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, | ||||
| +				  MTK_WED_RRO_MSDU_PG_DRV_CLR); | ||||
| + | ||||
| +		/* RRO Data Drv Reset */ | ||||
| +		wed_w32(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_CLR); | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_D_CFG(2), | ||||
| +				  MTK_WED_RRO_RX_D_DRV_CLR); | ||||
| +	} | ||||
| + | ||||
|  	/* reset route qm */ | ||||
|  	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); | ||||
|  	ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
|  				MTK_WED_CTRL_RX_ROUTE_QM_BUSY); | ||||
|  	if (ret) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM); | ||||
| +	} else if (mtk_wed_is_v3_or_greater(dev->hw)) { | ||||
| +		wed_set(dev, MTK_WED_RTQM_RST, BIT(0)); | ||||
| +		wed_clr(dev, MTK_WED_RTQM_RST, BIT(0)); | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM); | ||||
|  	} else { | ||||
| -		wed_set(dev, MTK_WED_RTQM_GLO_CFG, | ||||
| -			MTK_WED_RTQM_Q_RST); | ||||
| +		wed_set(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); | ||||
|  	} | ||||
|   | ||||
|  	/* reset tx wdma */ | ||||
| @@ -1473,8 +1655,12 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev) | ||||
|   | ||||
|  	/* reset tx wdma drv */ | ||||
|  	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN); | ||||
| -	mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
| -			  MTK_WED_CTRL_WDMA_INT_AGENT_BUSY); | ||||
| +	if (mtk_wed_is_v3_or_greater(dev->hw)) | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_WPDMA_STATUS, | ||||
| +				  MTK_WED_WPDMA_STATUS_TX_DRV); | ||||
| +	else | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
| +				  MTK_WED_CTRL_WDMA_INT_AGENT_BUSY); | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV); | ||||
|   | ||||
|  	/* reset wed rx dma */ | ||||
| @@ -1495,6 +1681,14 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev) | ||||
|  			  MTK_WED_CTRL_WED_RX_BM_BUSY); | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_RX_BM); | ||||
|   | ||||
| +	if (dev->wlan.hw_rro) { | ||||
| +		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN); | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
| +				  MTK_WED_CTRL_WED_RX_PG_BM_BUSY); | ||||
| +		wed_set(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM); | ||||
| +		wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM); | ||||
| +	} | ||||
| + | ||||
|  	/* wo change to enable state */ | ||||
|  	val = WO_STATE_ENABLE; | ||||
|  	ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, | ||||
| @@ -1549,16 +1743,55 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|   | ||||
|  	/* 2. Reset WDMA Rx DMA/Driver_Engine */ | ||||
|  	busy = !!mtk_wdma_rx_reset(dev); | ||||
| +	if (mtk_wed_is_v3_or_greater(dev->hw)) { | ||||
| +		val = MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE | | ||||
| +		      wed_r32(dev, MTK_WED_WDMA_GLO_CFG); | ||||
| +		val &= ~MTK_WED_WDMA_GLO_CFG_RX_DRV_EN; | ||||
| +		wed_w32(dev, MTK_WED_WDMA_GLO_CFG, val); | ||||
| +	} else { | ||||
| +		wed_clr(dev, MTK_WED_WDMA_GLO_CFG, | ||||
| +			MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); | ||||
| +	} | ||||
|   | ||||
| -	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); | ||||
|  	if (!busy) | ||||
|  		busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG, | ||||
|  					 MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY); | ||||
| +	if (!busy && mtk_wed_is_v3_or_greater(dev->hw)) | ||||
| +		busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG, | ||||
| +					 MTK_WED_WDMA_RX_PREF_BUSY); | ||||
|   | ||||
|  	if (busy) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV); | ||||
|  	} else { | ||||
| +		if (mtk_wed_is_v3_or_greater(dev->hw)) { | ||||
| +			/*1.a. Disable Prefetch HW*/ | ||||
| +			wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, | ||||
| +				MTK_WED_WDMA_RX_PREF_EN); | ||||
| +			mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG, | ||||
| +					  MTK_WED_WDMA_RX_PREF_BUSY); | ||||
| +			wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, | ||||
| +				MTK_WED_WDMA_RX_PREF_DDONE2_EN); | ||||
| + | ||||
| +			/* reset prefetch index */ | ||||
| +			wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, | ||||
| +				MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR | | ||||
| +				MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR); | ||||
| + | ||||
| +			wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, | ||||
| +				MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR | | ||||
| +				MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR); | ||||
| + | ||||
| +			/* reset prefetch FIFO */ | ||||
| +			wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, | ||||
| +				MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR | | ||||
| +				MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR); | ||||
| +			wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, 0); | ||||
| + | ||||
| +			/*2. Reset dma index*/ | ||||
| +			wed_w32(dev, MTK_WED_WDMA_RESET_IDX, | ||||
| +				MTK_WED_WDMA_RESET_IDX_RX_ALL); | ||||
| +		} | ||||
|  		wed_w32(dev, MTK_WED_WDMA_RESET_IDX, | ||||
|  			MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV); | ||||
|  		wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0); | ||||
| @@ -1574,8 +1807,13 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
|   | ||||
|  	for (i = 0; i < 100; i++) { | ||||
| -		val = wed_r32(dev, MTK_WED_TX_BM_INTF); | ||||
| -		if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40) | ||||
| +		if (mtk_wed_is_v1(dev->hw)) | ||||
| +			val = FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, | ||||
| +					wed_r32(dev, MTK_WED_TX_BM_INTF)); | ||||
| +		else | ||||
| +			val = FIELD_GET(MTK_WED_TX_TKID_INTF_TKFIFO_FDEP, | ||||
| +					wed_r32(dev, MTK_WED_TX_TKID_INTF)); | ||||
| +		if (val == 0x40) | ||||
|  			break; | ||||
|  	} | ||||
|   | ||||
| @@ -1599,6 +1837,8 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV); | ||||
| +		if (mtk_wed_is_v3_or_greater(dev->hw)) | ||||
| +			wed_w32(dev, MTK_WED_RX1_CTRL2, 0); | ||||
|  	} else { | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, | ||||
|  			MTK_WED_WPDMA_RESET_IDX_TX | | ||||
| @@ -1615,7 +1855,14 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) | ||||
|  		wed_w32(dev, MTK_WED_RESET_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| -	mtk_wed_rx_reset(dev); | ||||
| +	if (mtk_wed_is_v3_or_greater(dev->hw)) { | ||||
| +		/* reset amsdu engine */ | ||||
| +		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_TX_AMSDU); | ||||
| +	} | ||||
| + | ||||
| +	if (mtk_wed_get_rx_capa(dev)) | ||||
| +		mtk_wed_rx_reset(dev); | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| @@ -1932,7 +2179,7 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev) | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| -mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask) | ||||
| +mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask, bool reset) | ||||
|  { | ||||
|  	int i; | ||||
|   | ||||
| @@ -1942,6 +2189,12 @@ mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask) | ||||
|  	if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) | ||||
|  		return; | ||||
|   | ||||
| +	if (reset) { | ||||
| +		wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, | ||||
| +			MTK_WED_RRO_MSDU_PG_DRV_EN); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
|  	wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR); | ||||
|  	wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, | ||||
|  		MTK_WED_RRO_MSDU_PG_DRV_CLR); | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| index 0af264d..1ee0fe1 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h | ||||
| @@ -36,6 +36,8 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_RESET					0x008 | ||||
|  #define MTK_WED_RESET_TX_BM				BIT(0) | ||||
|  #define MTK_WED_RESET_RX_BM				BIT(1) | ||||
| +#define MTK_WED_RESET_RX_PG_BM				BIT(2) | ||||
| +#define MTK_WED_RESET_RRO_RX_TO_PG			BIT(3) | ||||
|  #define MTK_WED_RESET_TX_FREE_AGENT			BIT(4) | ||||
|  #define MTK_WED_RESET_WPDMA_TX_DRV			BIT(8) | ||||
|  #define MTK_WED_RESET_WPDMA_RX_DRV			BIT(9) | ||||
| @@ -58,7 +60,7 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY		BIT(3) | ||||
|  #define MTK_WED_CTRL_WED_RX_IND_CMD_EN			BIT(5) | ||||
|  #define MTK_WED_CTRL_WED_RX_PG_BM_EN			BIT(6) | ||||
| -#define MTK_WED_CTRL_WED_RX_PG_BM_BUSU			BIT(7) | ||||
| +#define MTK_WED_CTRL_WED_RX_PG_BM_BUSY			BIT(7) | ||||
|  #define MTK_WED_CTRL_WED_TX_BM_EN			BIT(8) | ||||
|  #define MTK_WED_CTRL_WED_TX_BM_BUSY			BIT(9) | ||||
|  #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN		BIT(10) | ||||
| @@ -117,6 +119,10 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_STATUS					0x060 | ||||
|  #define MTK_WED_STATUS_TX				GENMASK(15, 8) | ||||
|   | ||||
| +#define MTK_WED_WPDMA_STATUS				0x068 | ||||
| +#define MTK_WED_WPDMA_STATUS_TX_DRV			GENMASK(15, 8) | ||||
| + | ||||
| + | ||||
|  #define MTK_WED_TX_BM_CTRL				0x080 | ||||
|  #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM			GENMASK(6, 0) | ||||
|  #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM			GENMASK(22, 16) | ||||
| @@ -154,6 +160,9 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3		GENMASK(7, 0) | ||||
|  #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3		GENMASK(23, 16) | ||||
|   | ||||
| +#define MTK_WED_TX_TKID_INTF				0x0dc | ||||
| +#define MTK_WED_TX_TKID_INTF_TKFIFO_FDEP		GENMASK(25, 16) | ||||
| + | ||||
|  #define MTK_WED_TX_TKID_DYN_THR				0x0e0 | ||||
|  #define MTK_WED_TX_TKID_DYN_THR_LO			GENMASK(6, 0) | ||||
|  #define MTK_WED_TX_TKID_DYN_THR_HI			GENMASK(22, 16) | ||||
| @@ -205,6 +214,7 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_RING_RX_DATA(_n)			(0x420 + (_n) * 0x10) | ||||
|   | ||||
|  #define MTK_WED_SCR0					0x3c0 | ||||
| +#define MTK_WED_RX1_CTRL2				0x418 | ||||
|  #define MTK_WED_WPDMA_INT_TRIGGER			0x504 | ||||
|  #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE		BIT(1) | ||||
|  #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE		GENMASK(5, 4) | ||||
| @@ -320,6 +330,7 @@ struct mtk_wdma_desc { | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_D_RST_IDX			0x760 | ||||
|  #define MTK_WED_WPDMA_RX_D_RST_CRX_IDX			GENMASK(17, 16) | ||||
| +#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL		BIT(20) | ||||
|  #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX			GENMASK(25, 24) | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_GLO_CFG			0x76c | ||||
| @@ -336,6 +347,7 @@ struct mtk_wdma_desc { | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_CFG			0x7b4 | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_EN			BIT(0) | ||||
| +#define MTK_WED_WPDMA_RX_D_PREF_BUSY		BIT(1) | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE		GENMASK(12, 8) | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES		GENMASK(21, 16) | ||||
|   | ||||
| @@ -357,11 +369,13 @@ struct mtk_wdma_desc { | ||||
|   | ||||
|  #define MTK_WED_WDMA_RX_PREF_CFG			0x950 | ||||
|  #define MTK_WED_WDMA_RX_PREF_EN				BIT(0) | ||||
| +#define MTK_WED_WDMA_RX_PREF_BUSY			BIT(1) | ||||
|  #define MTK_WED_WDMA_RX_PREF_BURST_SIZE			GENMASK(12, 8) | ||||
|  #define MTK_WED_WDMA_RX_PREF_LOW_THRES			GENMASK(21, 16) | ||||
|  #define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR		BIT(24) | ||||
|  #define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR		BIT(25) | ||||
|  #define MTK_WED_WDMA_RX_PREF_DDONE2_EN			BIT(26) | ||||
| +#define MTK_WED_WDMA_RX_PREF_DDONE2_BUSY		BIT(27) | ||||
|   | ||||
|  #define MTK_WED_WDMA_RX_PREF_FIFO_CFG			0x95C | ||||
|  #define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR		BIT(0) | ||||
| @@ -390,6 +404,7 @@ struct mtk_wdma_desc { | ||||
|   | ||||
|  #define MTK_WED_WDMA_RESET_IDX				0xa08 | ||||
|  #define MTK_WED_WDMA_RESET_IDX_RX			GENMASK(17, 16) | ||||
| +#define MTK_WED_WDMA_RESET_IDX_RX_ALL			BIT(20) | ||||
|  #define MTK_WED_WDMA_RESET_IDX_DRV			GENMASK(25, 24) | ||||
|   | ||||
|  #define MTK_WED_WDMA_INT_CLR				0xa24 | ||||
| @@ -458,21 +473,66 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WDMA_INT_MASK_RX_DELAY			BIT(30) | ||||
|  #define MTK_WDMA_INT_MASK_RX_COHERENT			BIT(31) | ||||
|   | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG			0x238 | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR		BIT(0) | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR		BIT(4) | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR		BIT(8) | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR		BIT(12) | ||||
| + | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG			0x23c | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR		BIT(0) | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR		BIT(4) | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR		BIT(8) | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR		BIT(12) | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR		BIT(15) | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR		BIT(18) | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR		BIT(21) | ||||
| + | ||||
| + | ||||
| + | ||||
|  #define MTK_WDMA_INT_GRP1				0x250 | ||||
|  #define MTK_WDMA_INT_GRP2				0x254 | ||||
|   | ||||
|  #define MTK_WDMA_PREF_TX_CFG				0x2d0 | ||||
|  #define MTK_WDMA_PREF_TX_CFG_PREF_EN			BIT(0) | ||||
| +#define MTK_WDMA_PREF_TX_CFG_PREF_BUSY			BIT(1) | ||||
|   | ||||
|  #define MTK_WDMA_PREF_RX_CFG				0x2dc | ||||
|  #define MTK_WDMA_PREF_RX_CFG_PREF_EN			BIT(0) | ||||
| +#define MTK_WDMA_PREF_RX_CFG_PREF_BUSY			BIT(1) | ||||
| + | ||||
| +#define MTK_WDMA_PREF_RX_FIFO_CFG			0x2e0 | ||||
| +#define MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR		BIT(0) | ||||
| +#define MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR		BIT(16) | ||||
| + | ||||
| +#define MTK_WDMA_PREF_TX_FIFO_CFG			0x2d4 | ||||
| +#define MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR		BIT(0) | ||||
| +#define MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR		BIT(16) | ||||
| + | ||||
| +#define MTK_WDMA_PREF_SIDX_CFG				0x2e4 | ||||
| +#define MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR		GENMASK(3, 0) | ||||
| +#define MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR		GENMASK(5, 4) | ||||
|   | ||||
|  #define MTK_WDMA_WRBK_TX_CFG				0x300 | ||||
| +#define MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY			BIT(0) | ||||
|  #define MTK_WDMA_WRBK_TX_CFG_WRBK_EN			BIT(30) | ||||
|   | ||||
| +#define MTK_WDMA_WRBK_TX_FIFO_CFG(_n)			(0x304 + (_n) * 0x4) | ||||
| +#define MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR		BIT(0) | ||||
| + | ||||
| + | ||||
|  #define MTK_WDMA_WRBK_RX_CFG				0x344 | ||||
| +#define MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY			BIT(0) | ||||
|  #define MTK_WDMA_WRBK_RX_CFG_WRBK_EN			BIT(30) | ||||
|   | ||||
| +#define MTK_WDMA_WRBK_RX_FIFO_CFG(_n)			(0x348 + (_n) * 0x4) | ||||
| +#define MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR		BIT(0) | ||||
| + | ||||
| + | ||||
| +#define MTK_WDMA_WRBK_SIDX_CFG				0x388 | ||||
| +#define MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR		GENMASK(3, 0) | ||||
| +#define MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR		GENMASK(5, 4) | ||||
| + | ||||
|  #define MTK_PCIE_MIRROR_MAP(n)				((n) ? 0x4 : 0x0) | ||||
|  #define MTK_PCIE_MIRROR_MAP_EN				BIT(0) | ||||
|  #define MTK_PCIE_MIRROR_MAP_WED_ID			BIT(1) | ||||
| @@ -486,6 +546,9 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_RTQM_Q_DBG_BYPASS			BIT(5) | ||||
|  #define MTK_WED_RTQM_TXDMAD_FPORT			GENMASK(23, 20) | ||||
|   | ||||
| +#define MTK_WED_RTQM_RST				0xb04 | ||||
| + | ||||
| + | ||||
|  #define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT		0xb1c | ||||
|  #define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n)		(0xb20 + (_n) * 0x4) | ||||
|  #define	MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT			0xb28 | ||||
| @@ -675,6 +738,9 @@ struct mtk_wdma_desc { | ||||
|  #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR		BIT(17) | ||||
|  #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG	GENMASK(22, 18) | ||||
|   | ||||
| +#define MTK_WED_RRO_RX_HW_STS				0xf00 | ||||
| +#define MTK_WED_RX_IND_CMD_BUSY			GENMASK(31, 0) | ||||
| + | ||||
|  #define MTK_WED_RX_IND_CMD_CNT0				0xf20 | ||||
|  #define MTK_WED_RX_IND_CMD_DBG_CNT_EN			BIT(31) | ||||
|   | ||||
| diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h | ||||
| index e81e41f..83a4b8b 100644 | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h | ||||
| @@ -222,7 +222,7 @@ struct mtk_wed_ops { | ||||
|   | ||||
|  	u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask); | ||||
|  	void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask); | ||||
| -	void (*start_hw_rro)(struct mtk_wed_device *dev, u32 irq_mask); | ||||
| +	void (*start_hw_rro)(struct mtk_wed_device *dev, u32 irq_mask, bool reset); | ||||
|  	void (*rro_rx_ring_setup)(struct mtk_wed_device *dev, int ring, | ||||
|  				  void __iomem *regs); | ||||
|  	void (*msdu_pg_rx_ring_setup)(struct mtk_wed_device *dev, int ring, | ||||
| @@ -302,8 +302,8 @@ mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev) | ||||
|  #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev) | ||||
|  #define mtk_wed_device_setup_tc(_dev, _ndev, _type, _data) \ | ||||
|  	(_dev)->ops->setup_tc(_dev, _ndev, _type, _data) | ||||
| -#define mtk_wed_device_start_hw_rro(_dev, _mask) \ | ||||
| -	(_dev)->ops->start_hw_rro(_dev, _mask) | ||||
| +#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) \ | ||||
| +	(_dev)->ops->start_hw_rro(_dev, _mask, _reset) | ||||
|  #define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) \ | ||||
|  	(_dev)->ops->rro_rx_ring_setup(_dev, _ring, _regs) | ||||
|  #define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) \ | ||||
| @@ -329,7 +329,7 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) | ||||
|  #define mtk_wed_device_stop(_dev) do {} while (0) | ||||
|  #define mtk_wed_device_dma_reset(_dev) do {} while (0) | ||||
|  #define mtk_wed_device_setup_tc(_dev, _ndev, _type, _data) do {} while (0) | ||||
| -#define mtk_wed_device_start_hw_rro(_dev, _mask) do {} while (0) | ||||
| +#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) do {} while (0) | ||||
|  #define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) -ENODEV | ||||
|  #define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs)  -ENODEV | ||||
|  #define mtk_wed_device_ind_rx_ring_setup(_dev, _regs)  -ENODEV | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,140 +0,0 @@ | ||||
| From f83c601c7b525c0adedcf1f5e8041acd350c7e2d Mon Sep 17 00:00:00 2001 | ||||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 13:23:56 +0800 | ||||
| Subject: [PATCH] mtk: wed: add dma mask limitation and GFP_DMA32 for board >= | ||||
|  4GB dram | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed.c     | 16 +++++++++++----- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed_mcu.c |  4 ++-- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed_wo.c  |  4 ++-- | ||||
|  drivers/net/ethernet/mediatek/mtk_wed_wo.h  |  1 + | ||||
|  4 files changed, 16 insertions(+), 9 deletions(-) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| index 5eeb3ed..094bc94 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c | ||||
| @@ -625,7 +625,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  		void *buf; | ||||
|  		int s; | ||||
|   | ||||
| -		page = __dev_alloc_pages(GFP_KERNEL, 0); | ||||
| +		page = __dev_alloc_pages(GFP_KERNEL | GFP_DMA32, 0); | ||||
|  		if (!page) | ||||
|  			return -ENOMEM; | ||||
|   | ||||
| @@ -646,10 +646,11 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) | ||||
|   | ||||
|  		for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) { | ||||
|  			struct mtk_wdma_desc *desc = desc_ptr; | ||||
| +			u32 ctrl; | ||||
|   | ||||
|  			desc->buf0 = cpu_to_le32(buf_phys); | ||||
|  			if (!mtk_wed_is_v3_or_greater(dev->hw)) { | ||||
| -				u32 txd_size, ctrl; | ||||
| +				u32 txd_size; | ||||
|   | ||||
|  				txd_size = dev->wlan.init_buf(buf, buf_phys, | ||||
|  							      token++); | ||||
| @@ -663,11 +664,11 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  					ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG0 | | ||||
|  						FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2, | ||||
|  							   MTK_WED_BUF_SIZE - txd_size); | ||||
| -				desc->ctrl = cpu_to_le32(ctrl); | ||||
|  				desc->info = 0; | ||||
|  			} else { | ||||
| -				desc->ctrl = cpu_to_le32(token << 16); | ||||
| +				ctrl = token << 16 | TX_DMA_SDP1(buf_phys); | ||||
|  			} | ||||
| +			desc->ctrl = cpu_to_le32(ctrl); | ||||
|   | ||||
|  			desc_ptr += desc_size; | ||||
|  			buf += MTK_WED_BUF_SIZE; | ||||
| @@ -749,7 +750,7 @@ mtk_wed_hwrro_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  		void *buf; | ||||
|  		int s; | ||||
|   | ||||
| -		page = __dev_alloc_pages(GFP_KERNEL, 0); | ||||
| +		page = __dev_alloc_pages(GFP_KERNEL | GFP_DMA32, 0); | ||||
|  		if (!page) | ||||
|  			return -ENOMEM; | ||||
|   | ||||
| @@ -769,6 +770,7 @@ mtk_wed_hwrro_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  		buf_phys = page_phys; | ||||
|  		for (s = 0; s < MTK_WED_RX_PAGE_BUF_PER_PAGE; s++) { | ||||
|  			desc->buf0 = cpu_to_le32(buf_phys); | ||||
| +			desc->token = cpu_to_le32(RX_DMA_SDP1(buf_phys)); | ||||
|  			desc++; | ||||
|  			buf += MTK_WED_PAGE_BUF_SIZE; | ||||
|  			buf_phys += MTK_WED_PAGE_BUF_SIZE; | ||||
| @@ -2457,6 +2459,10 @@ mtk_wed_attach(struct mtk_wed_device *dev) | ||||
|  	dev->version = hw->version; | ||||
|  	dev->hw->pci_base = mtk_wed_get_pci_base(dev); | ||||
|   | ||||
| +	ret = dma_set_mask_and_coherent(hw->dev, DMA_BIT_MASK(32)); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
|  	if (hw->eth->dma_dev == hw->eth->dev && | ||||
|  	    of_dma_is_coherent(hw->eth->dev->of_node)) | ||||
|  		mtk_eth_set_dma_device(hw->eth, hw->dev); | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c | ||||
| index 18d1fb1..a88061c 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c | ||||
| @@ -145,7 +145,7 @@ int mtk_wed_exception_init(struct mtk_wed_wo *wo) | ||||
|  	}req; | ||||
|   | ||||
|  	exp->log_size = EXCEPTION_LOG_SIZE; | ||||
| -	exp->log = kmalloc(exp->log_size, GFP_ATOMIC); | ||||
| +	exp->log = page_frag_alloc(&wo->page, exp->log_size, GFP_ATOMIC | GFP_DMA32); | ||||
|  	if (!exp->log) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| @@ -165,7 +165,7 @@ int mtk_wed_exception_init(struct mtk_wed_wo *wo) | ||||
|  				    &req, sizeof(req), false); | ||||
|   | ||||
|  free: | ||||
| -	kfree(exp->log); | ||||
| +	skb_free_frag(exp->log); | ||||
|  	return -ENOMEM; | ||||
|  } | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c | ||||
| index 54b7787..e991d20 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c | ||||
| @@ -88,7 +88,7 @@ woif_q_rx_fill(struct mtk_wed_wo *wo, struct wed_wo_queue *q, bool rx) | ||||
|  		page = &q->rx_page; | ||||
|   | ||||
|  	while (q->queued < q->ndesc) { | ||||
| -		buf = page_frag_alloc(page, len, GFP_ATOMIC); | ||||
| +		buf = page_frag_alloc(page, len, GFP_ATOMIC | GFP_DMA32); | ||||
|  		if (!buf) | ||||
|  			break; | ||||
|   | ||||
| @@ -555,7 +555,7 @@ void mtk_wed_wo_exit(struct mtk_wed_hw *hw) | ||||
|   | ||||
|  	if (wo->exp.log) { | ||||
|  		dma_unmap_single(wo->hw->dev, wo->exp.phys, wo->exp.log_size, DMA_FROM_DEVICE); | ||||
| -		kfree(wo->exp.log); | ||||
| +		skb_free_frag(wo->exp.log); | ||||
|  	} | ||||
|   | ||||
|  	wo->hw = NULL; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h | ||||
| index b24fef3..5afa6de 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h | ||||
| @@ -193,6 +193,7 @@ struct mtk_wed_wo { | ||||
|  	const struct wed_wo_drv_ops *drv_ops; | ||||
|  	const struct wed_wo_mcu_ops *mcu_ops; | ||||
|  	const struct wed_wo_queue_ops *queue_ops; | ||||
| +	struct page_frag_cache page; | ||||
|   | ||||
|  	struct net_device napi_dev; | ||||
|  	spinlock_t rx_lock; | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,124 +0,0 @@ | ||||
| From e917fdd9042787edd43e4070c3480baf0dfb8d33 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Mon, 18 Mar 2024 16:57:13 +0800 | ||||
| Subject: [PATCH 22/24]  | ||||
|  999-3022-mediatek-ethernet-add-multiple-ppe-allocatiion.patch | ||||
|  | ||||
| --- | ||||
|  arch/arm64/boot/dts/mediatek/mt7988.dtsi      |  1 + | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.c   | 22 ++++++++++++++++--- | ||||
|  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  3 +++ | ||||
|  .../net/ethernet/mediatek/mtk_ppe_offload.c   | 10 +++++++++ | ||||
|  4 files changed, 33 insertions(+), 3 deletions(-) | ||||
|  | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7988.dtsi b/arch/arm64/boot/dts/mediatek/mt7988.dtsi | ||||
| index 4a1a446..57b27f8 100644 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7988.dtsi | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7988.dtsi | ||||
| @@ -942,6 +942,7 @@ | ||||
|  		mediatek,infracfg = <&topmisc>; | ||||
|  		mediatek,toprgu = <&watchdog>; | ||||
|  		mediatek,hwver = <&hwver>; | ||||
| +		mtketh-ppe-num = <3>; | ||||
|  		#reset-cells = <1>; | ||||
|  		#address-cells = <1>; | ||||
|  		#size-cells = <0>; | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| index 291afdc..c0f4ade 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c | ||||
| @@ -2349,6 +2349,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, | ||||
|  	u8 *data, *new_data; | ||||
|  	struct mtk_rx_dma_v2 *rxd, trxd; | ||||
|  	int done = 0; | ||||
| +	int i; | ||||
|   | ||||
|  	if (unlikely(!ring)) | ||||
|  		goto rx_done; | ||||
| @@ -2463,8 +2464,10 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, | ||||
|  		} | ||||
|  #endif | ||||
|   | ||||
| -		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) | ||||
| -			mtk_ppe_check_skb(eth->ppe[0], skb, hash); | ||||
| +		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) { | ||||
| +			i = eth->mac[mac]->ppe_idx; | ||||
| +			mtk_ppe_check_skb(eth->ppe[i], skb, hash); | ||||
| +		} | ||||
|   | ||||
|  		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { | ||||
|  			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { | ||||
| @@ -4113,7 +4116,19 @@ static int mtk_open(struct net_device *dev) | ||||
|  			     SGMSYS_QPHY_PWR_STATE_CTRL, 0); | ||||
|   | ||||
|  	if (eth->soc->offload_version) { | ||||
| -			gdm_config = MTK_GDMA_TO_PPE0; | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +			if (eth->ppe_num >= 3 && mac->id == 2) { | ||||
| +				mac->ppe_idx = 2; | ||||
| +				gdm_config = MTK_GDMA_TO_PPE2; | ||||
| +			} else if (eth->ppe_num >= 2 && mac->id == 1) { | ||||
| +				mac->ppe_idx = 1; | ||||
| +				gdm_config = MTK_GDMA_TO_PPE1; | ||||
| +			} else | ||||
| +#endif | ||||
| +			{ | ||||
| +				mac->ppe_idx = 0; | ||||
| +				gdm_config = MTK_GDMA_TO_PPE0; | ||||
| +			} | ||||
|   | ||||
|  			for (i = 0; i < eth->ppe_num; i++) | ||||
|  				mtk_ppe_start(eth->ppe[i]); | ||||
| @@ -5129,6 +5144,7 @@ static const struct net_device_ops mtk_netdev_ops = { | ||||
|  	.ndo_poll_controller	= mtk_poll_controller, | ||||
|  #endif | ||||
|  	.ndo_setup_tc		= mtk_eth_setup_tc, | ||||
| +	.ndo_fill_receive_path	= mtk_eth_fill_receive_path, | ||||
|  }; | ||||
|   | ||||
|  static void mux_poll(struct work_struct *work) | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| index 8454361..d958aec 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h | ||||
| @@ -1978,6 +1978,7 @@ struct mtk_mac { | ||||
|  	phy_interface_t			interface; | ||||
|  	unsigned int			mode; | ||||
|  	unsigned int			type; | ||||
| +	unsigned int			ppe_idx; | ||||
|  	int				speed; | ||||
|  	struct device_node		*of_node; | ||||
|  	struct phylink			*phylink; | ||||
| @@ -2079,6 +2080,8 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, | ||||
|  		     void *type_data); | ||||
|  int mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f, | ||||
|  			   struct mtk_eth *eth); | ||||
| +int mtk_eth_fill_receive_path(struct net_device_path_ctx *ctx, | ||||
| +			      struct net_device_path *path); | ||||
|  void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); | ||||
|  u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index); | ||||
|   | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| index 402f1e3..3bfbec8 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -828,6 +828,16 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +int mtk_eth_fill_receive_path(struct net_device_path_ctx *ctx, | ||||
| +			      struct net_device_path *path) | ||||
| +{ | ||||
| +	struct mtk_mac *mac = netdev_priv(ctx->dev); | ||||
| + | ||||
| +	path->mtk_wdma.wdma_idx = mac->ppe_idx; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  int mtk_eth_offload_init(struct mtk_eth *eth, int id) | ||||
|  { | ||||
|  	if (!eth->ppe[id] || !eth->ppe[id]->foe_table) | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,58 +0,0 @@ | ||||
| From e8a6054c824c5c65abd501bfd7437a3d7c5f2751 Mon Sep 17 00:00:00 2001 | ||||
| From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> | ||||
| Date: Tue, 28 Nov 2023 13:25:03 +0800 | ||||
| Subject: [PATCH 23/24]  | ||||
|  mtk-ppe-dispatch-short-packets-to-high-priority-TXQ-in-PPPQ | ||||
|  | ||||
| --- | ||||
|  drivers/net/ethernet/mediatek/mtk_ppe.c | 27 +++++++++++++++++++++++++ | ||||
|  1 file changed, 27 insertions(+) | ||||
|  | ||||
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| index ae0acd5..547b5a0 100644 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -451,6 +451,31 @@ int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid) | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +void mtk_foe_entry_adjust_qid(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
| +{ | ||||
| +	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(&entry->data); | ||||
| +	u32 *ib2 = mtk_foe_entry_ib2(&entry->data); | ||||
| +	u8 qid; | ||||
| + | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +	if (l2->tport_id != 1) | ||||
| +		return; | ||||
| +#else | ||||
| +	if (!(*ib2 & MTK_FOE_IB2_PSE_QOS)) | ||||
| +		return; | ||||
| +#endif | ||||
| + | ||||
| +	qid = FIELD_GET(MTK_FOE_IB2_QID, *ib2); | ||||
| +	/* To enhance performance in the unbalanced PHY rate test, | ||||
| +	 * dispatching short packets to the high priority TXQ. | ||||
| +	 */ | ||||
| +	if (ppe->eth->qos_toggle == 2 && qid < 6) { | ||||
| +		qid += 6; | ||||
| +		*ib2 &= ~MTK_FOE_IB2_QID; | ||||
| +		*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, qid); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  int mtk_foe_entry_set_dscp(struct mtk_foe_entry *entry, int dscp) | ||||
|  { | ||||
|  	u32 *ib2 = mtk_foe_entry_ib2(entry); | ||||
| @@ -790,6 +815,9 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| +		if (skb && skb->len < 100) | ||||
| +			mtk_foe_entry_adjust_qid(ppe, entry); | ||||
| + | ||||
|  		entry->hash = hash; | ||||
|  		__mtk_foe_entry_commit(ppe, &entry->data, hash); | ||||
|  		found = true; | ||||
| --  | ||||
| 2.18.0 | ||||
| @@ -1,38 +0,0 @@ | ||||
| From b294202119468041b6f4b6651f968c9d2a0adcc2 Mon Sep 17 00:00:00 2001 | ||||
| From: Rex Lu <rex.lu@mediatek.com> | ||||
| Date: Mon, 22 Jan 2024 13:50:29 +0800 | ||||
| Subject: [PATCH 24/24] dts 88d option type 2 support | ||||
|  | ||||
| Signed-off-by: Rex Lu <rex.lu@mediatek.com> | ||||
| --- | ||||
|  arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts | 1 + | ||||
|  arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts | 1 + | ||||
|  2 files changed, 2 insertions(+) | ||||
|  | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts b/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts | ||||
| index 8ebcbaf..df15051 100644 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts | ||||
| @@ -572,6 +572,7 @@ | ||||
|  	mt7996@0,0 { | ||||
|  		reg = <0x0000 0 0 0 0>; | ||||
|  		device_type = "pci"; | ||||
| +		option_type = <2>; | ||||
|  		mediatek,mtd-eeprom = <&factory 0x0>; | ||||
|  	}; | ||||
|  }; | ||||
| diff --git a/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts b/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts | ||||
| index d4f96c8..c27ffa6 100644 | ||||
| --- a/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts | ||||
| +++ b/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts | ||||
| @@ -547,6 +547,7 @@ | ||||
|  	mt7996@0,0 { | ||||
|  		reg = <0x0000 0 0 0 0>; | ||||
|  		device_type = "pci"; | ||||
| +		option_type = <2>; | ||||
|  		mediatek,mtd-eeprom = <&factory 0x0>; | ||||
|  	}; | ||||
|  }; | ||||
| --  | ||||
| 2.18.0 | ||||
|  | ||||
| @@ -1,63 +0,0 @@ | ||||
| Index: linux-5.4.246/drivers/char/tpm/tpm_tis_spi.c | ||||
| =================================================================== | ||||
| --- linux-5.4.246.orig/drivers/char/tpm/tpm_tis_spi.c | ||||
| +++ linux-5.4.246/drivers/char/tpm/tpm_tis_spi.c | ||||
| @@ -199,11 +199,35 @@ static const struct tpm_tis_phy_ops tpm_ | ||||
|  	.do_calibration = tpm_tis_spi_do_calibration, | ||||
|  }; | ||||
|   | ||||
| +int reset_tpm(struct spi_device *dev) | ||||
| +{ | ||||
| +	int error; | ||||
| +	struct gpio_desc *reset_gpio; | ||||
| +	reset_gpio = gpiod_get_optional(&dev->dev, "reset", GPIOD_OUT_LOW); | ||||
| +	error = PTR_ERR_OR_ZERO(reset_gpio); | ||||
| + | ||||
| +	printk("Doing tpm reset!!"); | ||||
| + | ||||
| +	if(error) { | ||||
| +		printk("get tpm reset gpio fail!!!!!\n"); | ||||
| +		return error; | ||||
| +	} | ||||
| + | ||||
| +	if(reset_gpio) | ||||
| +		gpiod_set_consumer_name(reset_gpio, "TPM reset"); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int tpm_tis_spi_probe(struct spi_device *dev) | ||||
|  { | ||||
|  	struct tpm_tis_spi_phy *phy; | ||||
|  	int irq; | ||||
|   | ||||
| +	if(reset_tpm(dev)){ | ||||
| +		printk("!!!tpm reset fail!!\n"); | ||||
| +	} | ||||
| + | ||||
|  	phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy), | ||||
|  			   GFP_KERNEL); | ||||
|  	if (!phy) | ||||
| Index: linux-5.4.246/drivers/spi/spi.c | ||||
| =================================================================== | ||||
| --- linux-5.4.246.orig/drivers/spi/spi.c | ||||
| +++ linux-5.4.246/drivers/spi/spi.c | ||||
| @@ -1124,8 +1124,17 @@ int spi_do_calibration(struct spi_contro | ||||
|  	bool hit; | ||||
|   | ||||
|  	/* Make sure we can start calibration */ | ||||
| +#if 1 | ||||
| +	if(!ctlr->cal_target || !ctlr->cal_rule) { | ||||
| +		return 0; | ||||
| +	} else if(!ctlr->append_caldata) { | ||||
| +		pr_err("%s: calibration is enabled but no controller data.\n", __func__); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| +#else | ||||
|  	if(!ctlr->cal_target || !ctlr->cal_rule || !ctlr->append_caldata) | ||||
|  		return -EINVAL; | ||||
| +#endif | ||||
|  	datalen = ctlr->cal_rule->datalen; | ||||
|  	addrlen = ctlr->cal_rule->addrlen; | ||||
|   | ||||
|  | ||||
| @@ -0,0 +1,24 @@ | ||||
| --- a/include/net/netfilter/nf_flow_table.h | ||||
| +++ b/include/net/netfilter/nf_flow_table.h | ||||
| @@ -179,10 +179,21 @@ struct flow_offload { | ||||
|  	struct rcu_head				rcu_head; | ||||
|  }; | ||||
|   | ||||
| +enum flow_offload_tnl { | ||||
| +	FLOW_OFFLOAD_TNL_GRETAP, | ||||
| +	FLOW_OFFLOAD_TNL_PPTP, | ||||
| +	FLOW_OFFLOAD_TNL_L2TP_V2, | ||||
| +	FLOW_OFFLOAD_TNL_L2TP_V3, | ||||
| +	FLOW_OFFLOAD_TNL_VXLAN, | ||||
| +	FLOW_OFFLOAD_NATT, | ||||
| +	__FLOW_OFFLOAD_MAX, | ||||
| +}; | ||||
| + | ||||
|  struct flow_offload_hw_path { | ||||
|  	struct net_device *dev; | ||||
|  	struct net_device *virt_dev; | ||||
|  	u32 flags; | ||||
| +	u32 tnl_type; | ||||
|   | ||||
|  	u8 eth_src[ETH_ALEN]; | ||||
|  	u8 eth_dest[ETH_ALEN]; | ||||
| @@ -0,0 +1,410 @@ | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_hnat/Makefile | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_hnat/Makefile | ||||
| @@ -1,7 +1,9 @@ | ||||
|  ccflags-y=-Werror | ||||
|   | ||||
|  obj-$(CONFIG_NET_MEDIATEK_HNAT)         += mtkhnat.o | ||||
| -mtkhnat-objs := hnat.o hnat_nf_hook.o hnat_debugfs.o hnat_mcast.o | ||||
| +mtkhnat-objs := hnat.o hnat_nf_hook.o hnat_debugfs.o hnat_mcast.o \ | ||||
| +		external/hnat_entry.o | ||||
| +mtkhnat-$(CONFIG_MEDIATEK_NETSYS_V3) += external/v3/hnat_ipv4_hnapt.o | ||||
|  ifeq ($(CONFIG_NET_DSA_AN8855), y) | ||||
|  mtkhnat-y	+= hnat_stag.o | ||||
|  else | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c | ||||
| @@ -2919,6 +2919,7 @@ u32 hnat_get_ppe_hash(struct foe_entry * | ||||
|   | ||||
|  	return hash; | ||||
|  } | ||||
| +EXPORT_SYMBOL(hnat_get_ppe_hash); | ||||
|   | ||||
|  static u32 hnat_char2hex(const char c) | ||||
|  { | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_hnat/external/hnat_entry.c | ||||
| @@ -0,0 +1,61 @@ | ||||
| +/*   This program is free software; you can redistribute it and/or modify | ||||
| + *   it under the terms of the GNU General Public License as published by | ||||
| + *   the Free Software Foundation; version 2 of the License | ||||
| + * | ||||
| + *   This program is distributed in the hope that it will be useful, | ||||
| + *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| + *   GNU General Public License for more details. | ||||
| + * | ||||
| + *   Copyright (C) 2024 Frank-zj Lin <frank-zj.lin@mediatek.com> | ||||
| + */ | ||||
| +#include <linux/bits.h> | ||||
| + | ||||
| +#include "../hnat.h" | ||||
| +#include "hnat_entry.h" | ||||
| + | ||||
| +static inline int ppe_id_get_by_sport(u32 sp) | ||||
| +{ | ||||
| +        if (CFG_PPE_NUM == 3) { | ||||
| +                switch (sp) { | ||||
| +                case NR_GMAC1_PORT: | ||||
| +                        return 0; | ||||
| +                case NR_GMAC2_PORT: | ||||
| +                        return 1; | ||||
| +                case NR_GMAC3_PORT: | ||||
| +                        return 2; | ||||
| +                } | ||||
| +        } | ||||
| + | ||||
| +        return 0; | ||||
| +} | ||||
| + | ||||
| +void hnat_entry_add(struct foe_entry *foe) | ||||
| +{ | ||||
| +        struct foe_entry *dst_foe; | ||||
| +        u32 ppe_id; | ||||
| +        int hash; | ||||
| +	int i; | ||||
| + | ||||
| +        hash = hnat_get_ppe_hash(foe); | ||||
| +        ppe_id = ppe_id_get_by_sport(foe->ipv4_hnapt.bfib1.sp); | ||||
| + | ||||
| +	/* collision policy: at most 4 entries with the same hash value */ | ||||
| +	for (i = 0; i < 4; i++) { | ||||
| +		dst_foe = hnat_get_foe_entry(ppe_id, | ||||
| +					     ((hash + i) % hnat_priv->foe_etry_num)); | ||||
| + | ||||
| +	        if ((IS_ERR(dst_foe))) | ||||
| +	                return; | ||||
| + | ||||
| +		if (dst_foe->ipv4_hnapt.bfib1.state == BIND) | ||||
| +			continue; | ||||
| + | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +        /* We must ensure all info has been updated before set to hw */ | ||||
| +        wmb(); | ||||
| +        memcpy(dst_foe, foe, sizeof(*foe)); | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_entry_add); | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_hnat/external/hnat_entry.h | ||||
| @@ -0,0 +1,45 @@ | ||||
| +/*   This program is free software; you can redistribute it and/or modify | ||||
| + *   it under the terms of the GNU General Public License as published by | ||||
| + *   the Free Software Foundation; version 2 of the License | ||||
| + * | ||||
| + *   This program is distributed in the hope that it will be useful, | ||||
| + *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| + *   GNU General Public License for more details. | ||||
| + * | ||||
| + *   Copyright (C) 2024 Frank-zj Lin <frank-zj.lin@mediatek.com> | ||||
| + */ | ||||
| + | ||||
| +#ifndef _MTK_HNAT_EXTERNAL_HNAT_ENTRY_H_ | ||||
| +#define _MTK_HNAT_EXTERNAL_HNAT_ENTRY_H_ | ||||
| + | ||||
| +#include <linux/debugfs.h> | ||||
| + | ||||
| +#include "../hnat.h" | ||||
| + | ||||
| +void hnat_entry_add(struct foe_entry *foe); | ||||
| + | ||||
| +/* necessary apis to access struct hnapt_ipv4_hnapt from external kernel module */ | ||||
| +int hnat_ipv4_hnapt_init(struct hnat_ipv4_hnapt *entry); | ||||
| +int hnat_ipv4_hnapt_sp_set(struct hnat_ipv4_hnapt *entry, u32 val); | ||||
| +int hnat_ipv4_hnapt_qid_set(struct hnat_ipv4_hnapt *entry, u32 val); | ||||
| +int hnat_ipv4_hnapt_fp_set(struct hnat_ipv4_hnapt *entry, u32 val); | ||||
| +int hnat_ipv4_hnapt_sip_set(struct hnat_ipv4_hnapt *entry, __be32 val); | ||||
| +int hnat_ipv4_hnapt_dip_set(struct hnat_ipv4_hnapt *entry, __be32 val); | ||||
| +int hnat_ipv4_hnapt_dport_set(struct hnat_ipv4_hnapt *entry, u16 val); | ||||
| +int hnat_ipv4_hnapt_sport_set(struct hnat_ipv4_hnapt *entry, u16 val); | ||||
| +int hnat_ipv4_hnapt_new_sip_set(struct hnat_ipv4_hnapt *entry, __be32 val); | ||||
| +int hnat_ipv4_hnapt_new_dip_set(struct hnat_ipv4_hnapt *entry, __be32 val); | ||||
| +int hnat_ipv4_hnapt_new_dport_set(struct hnat_ipv4_hnapt *entry, u16 val); | ||||
| +int hnat_ipv4_hnapt_new_sport_set(struct hnat_ipv4_hnapt *entry, u16 val); | ||||
| +int hnat_ipv4_hnapt_dmac_set(struct hnat_ipv4_hnapt *entry, u8 *mac); | ||||
| +int hnat_ipv4_hnapt_smac_set(struct hnat_ipv4_hnapt *entry, u8 *mac); | ||||
| +#if defined(CONFIG_MEDIATEK_NETSYS_V3) | ||||
| +int hnat_ipv4_hnapt_cdrt_id_set(struct hnat_ipv4_hnapt *entry, u32 val); | ||||
| +u32 hnat_ipv4_hnapt_tops_entry_get(struct hnat_ipv4_hnapt *entry); | ||||
| +int hnat_ipv4_hnapt_tops_entry_set(struct hnat_ipv4_hnapt *entry, u32 val); | ||||
| +u32 hnat_ipv4_hnapt_tport_id_get(struct hnat_ipv4_hnapt *entry); | ||||
| +int hnat_ipv4_hnapt_tport_id_set(struct hnat_ipv4_hnapt *entry, u32 val); | ||||
| +#endif /* (CONFIG_MEDIATEK_NETSYS_V3) */ | ||||
| + | ||||
| +#endif /* _MTK_HNAT_EXTERNAL_HNAT_ENTRY_H_ */ | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_hnat/external/v3/hnat_ipv4_hnapt.c | ||||
| @@ -0,0 +1,272 @@ | ||||
| +/*   This program is free software; you can redistribute it and/or modify | ||||
| + *   it under the terms of the GNU General Public License as published by | ||||
| + *   the Free Software Foundation; version 2 of the License | ||||
| + * | ||||
| + *   This program is distributed in the hope that it will be useful, | ||||
| + *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| + *   GNU General Public License for more details. | ||||
| + * | ||||
| + *   Copyright (C) 2024 Frank-zj Lin <frank-zj.lin@mediatek.com> | ||||
| + */ | ||||
| +#include <linux/bits.h> | ||||
| + | ||||
| +#include "../hnat_entry.h" | ||||
| + | ||||
| +/* Initialize with default values of a statically bind entry */ | ||||
| +static int hnat_bind_info_blk_init(struct hnat_bind_info_blk* blk) | ||||
| +{ | ||||
| +	if (!blk) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	blk->pkt_type	= IPV4_HNAPT; | ||||
| +	blk->cah	= 0x1; | ||||
| +	blk->ttl	= 0x1; | ||||
| +	blk->state	= 0x2; /* bind */ | ||||
| +	blk->sta	= 0x1; /* static entry */ | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/* PSE source port 0~15 */ | ||||
| +static int hnat_bind_info_blk_sp_set(struct hnat_bind_info_blk* blk, u32 val) | ||||
| +{ | ||||
| +	u32 _val = val & GENMASK(3, 0); | ||||
| + | ||||
| +	if (!blk || _val != val) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	blk->sp = _val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/* Initialize with default values of a statically bind entry */ | ||||
| +static int hnat_info_blk2_init(struct hnat_info_blk2* blk) | ||||
| +{ | ||||
| +	if (!blk) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	blk->mibf	= 0x1; | ||||
| +	blk->port_ag	= 0xF; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int hnat_info_blk2_qid_set(struct hnat_info_blk2* blk, u32 val) | ||||
| +{ | ||||
| +	u32 _val = val & GENMASK(6, 0); | ||||
| + | ||||
| +	if (!blk || _val != val) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	blk->qid = _val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/* PSE final port 0~15 */ | ||||
| +static int hnat_info_blk2_fp_set(struct hnat_info_blk2* blk, u32 val) | ||||
| +{ | ||||
| +	u32 _val = val & GENMASK(3, 0); | ||||
| + | ||||
| +	if (!blk || _val != val) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	blk->dp = _val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +int hnat_ipv4_hnapt_init(struct hnat_ipv4_hnapt *entry) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	hnat_bind_info_blk_init(&entry->bfib1); | ||||
| +	hnat_info_blk2_init(&entry->iblk2); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_init); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_sp_set(struct hnat_ipv4_hnapt *entry, u32 val) | ||||
| +{ | ||||
| +	return hnat_bind_info_blk_sp_set(&entry->bfib1, val); | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_sp_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_qid_set(struct hnat_ipv4_hnapt *entry, u32 val) | ||||
| +{ | ||||
| +	return hnat_info_blk2_qid_set(&entry->iblk2, val); | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_qid_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_fp_set(struct hnat_ipv4_hnapt *entry, u32 val) | ||||
| +{ | ||||
| +	return hnat_info_blk2_fp_set(&entry->iblk2, val); | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_fp_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_sip_set(struct hnat_ipv4_hnapt *entry, __be32 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->sip = ntohl(val); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_sip_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_dip_set(struct hnat_ipv4_hnapt *entry, __be32 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->dip = ntohl(val); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_dip_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_dport_set(struct hnat_ipv4_hnapt *entry, u16 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->dport = val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_dport_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_sport_set(struct hnat_ipv4_hnapt *entry, u16 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->sport = val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_sport_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_new_sip_set(struct hnat_ipv4_hnapt *entry, __be32 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->new_sip = ntohl(val); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_new_sip_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_new_dip_set(struct hnat_ipv4_hnapt *entry, __be32 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->new_dip = ntohl(val); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_new_dip_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_new_dport_set(struct hnat_ipv4_hnapt *entry, u16 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->new_dport = val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_new_dport_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_new_sport_set(struct hnat_ipv4_hnapt *entry, u16 val) | ||||
| +{ | ||||
| +	if (!entry) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->new_sport = val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_new_sport_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_cdrt_id_set(struct hnat_ipv4_hnapt *entry, u32 val) | ||||
| +{ | ||||
| +	u32 _val = val & GENMASK(7, 0); | ||||
| + | ||||
| +	if (!entry || _val != val) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->cdrt_id = _val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_cdrt_id_set); | ||||
| + | ||||
| +u32 hnat_ipv4_hnapt_tops_entry_get(struct hnat_ipv4_hnapt *entry) | ||||
| +{ | ||||
| +	return entry->tops_entry; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_tops_entry_get); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_tops_entry_set(struct hnat_ipv4_hnapt *entry, u32 val) | ||||
| +{ | ||||
| +	u32 _val = val & GENMASK(5, 0); | ||||
| + | ||||
| +	if (!entry || _val != val) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->tops_entry = _val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_tops_entry_set); | ||||
| + | ||||
| +u32 hnat_ipv4_hnapt_tport_id_get(struct hnat_ipv4_hnapt *entry) | ||||
| +{ | ||||
| +	return entry->tport_id; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_tport_id_get); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_tport_id_set(struct hnat_ipv4_hnapt *entry, u32 val) | ||||
| +{ | ||||
| +	u32 _val = val & GENMASK(3, 0); | ||||
| + | ||||
| +	if (!entry || _val != val) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->tport_id = _val; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_tport_id_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_dmac_set(struct hnat_ipv4_hnapt *entry, u8 *mac) | ||||
| +{ | ||||
| +	if (!entry || !mac) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->dmac_hi = swab32(*((u32 *)mac)); | ||||
| +	entry->dmac_lo = swab16(*((u16 *)&mac[4])); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_dmac_set); | ||||
| + | ||||
| +int hnat_ipv4_hnapt_smac_set(struct hnat_ipv4_hnapt *entry, u8 *mac) | ||||
| +{ | ||||
| +	if (!entry || !mac) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	entry->smac_hi = swab32(*((u32 *)mac)); | ||||
| +	entry->smac_lo = swab16(*((u16 *)&mac[4])); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(hnat_ipv4_hnapt_smac_set); | ||||
| @@ -0,0 +1,45 @@ | ||||
| --- a/net/ipv4/ip_gre.c | ||||
| +++ b/net/ipv4/ip_gre.c | ||||
| @@ -39,6 +39,7 @@ | ||||
|  #include <net/inet_ecn.h> | ||||
|  #include <net/xfrm.h> | ||||
|  #include <net/net_namespace.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
|  #include <net/netns/generic.h> | ||||
|  #include <net/rtnetlink.h> | ||||
|  #include <net/gre.h> | ||||
| @@ -901,6 +902,24 @@ static int ipgre_close(struct net_device | ||||
|  } | ||||
|  #endif | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int gre_dev_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *dev = path->dev; | ||||
| +	struct ip_tunnel *tunnel = netdev_priv(dev); | ||||
| + | ||||
| +	if (path->flags & BIT(DEV_PATH_TNL)) | ||||
| +		return -EEXIST; | ||||
| + | ||||
| +	path->flags |= BIT(DEV_PATH_TNL); | ||||
| +	path->tnl_type = FLOW_OFFLOAD_TNL_GRETAP; | ||||
| +	path->virt_dev = dev; | ||||
| +	path->dev = tunnel->dev; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct net_device_ops ipgre_netdev_ops = { | ||||
|  	.ndo_init		= ipgre_tunnel_init, | ||||
|  	.ndo_uninit		= ip_tunnel_uninit, | ||||
| @@ -1264,6 +1283,9 @@ static const struct net_device_ops gre_t | ||||
|  	.ndo_get_stats64	= ip_tunnel_get_stats64, | ||||
|  	.ndo_get_iflink		= ip_tunnel_get_iflink, | ||||
|  	.ndo_fill_metadata_dst	= gre_fill_metadata_dst, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check = gre_dev_flow_offload_check, | ||||
| +#endif | ||||
|  }; | ||||
|   | ||||
|  static int erspan_tunnel_init(struct net_device *dev) | ||||
| @@ -0,0 +1,65 @@ | ||||
| --- a/drivers/net/ppp/ppp_generic.c | ||||
| +++ b/drivers/net/ppp/ppp_generic.c | ||||
| @@ -296,6 +296,9 @@ static void unit_put(struct idr *p, int | ||||
|  static void *unit_find(struct idr *p, int n); | ||||
|  static void ppp_setup(struct net_device *dev); | ||||
|   | ||||
| +struct sock *ppp_netdev_get_sock(struct net_device *dev); | ||||
| +EXPORT_SYMBOL(ppp_netdev_get_sock); | ||||
| + | ||||
|  static const struct net_device_ops ppp_netdev_ops; | ||||
|   | ||||
|  static struct class *ppp_class; | ||||
| @@ -1660,6 +1663,40 @@ ppp_send_frame(struct ppp *ppp, struct s | ||||
|  	++ppp->dev->stats.tx_errors; | ||||
|  } | ||||
|   | ||||
| +struct sock *ppp_netdev_get_sock(struct net_device *dev) | ||||
| +{ | ||||
| +	struct list_head *list; | ||||
| +	struct channel *pch; | ||||
| +	struct ppp *ppp; | ||||
| +	struct sock *sk; | ||||
| + | ||||
| +	if (!dev) | ||||
| +		return ERR_PTR(-EINVAL); | ||||
| + | ||||
| +	ppp = netdev_priv(dev); | ||||
| + | ||||
| +	list = &ppp->channels; | ||||
| +	if (list_empty(list)) | ||||
| +		 /* nowhere to send the packet */ | ||||
| +		return ERR_PTR(-EINVAL); | ||||
| + | ||||
| +	if (ppp->flags & SC_MULTILINK) | ||||
| +		/* not doing multilink: send it down the first channel */ | ||||
| +		return ERR_PTR(-EPERM); | ||||
| + | ||||
| +	list = list->next; | ||||
| +	pch = list_entry(list, struct channel, clist); | ||||
| + | ||||
| +	spin_lock(&pch->downl); | ||||
| +	if (pch->chan) | ||||
| +		sk = (struct sock *)pch->chan->private; | ||||
| +	else | ||||
| +		sk = ERR_PTR(-EINVAL); | ||||
| +	spin_unlock(&pch->downl); | ||||
| + | ||||
| +	return sk; | ||||
| +} | ||||
| + | ||||
|  /* | ||||
|   * Try to send the frame in xmit_pending. | ||||
|   * The caller should have the xmit path locked. | ||||
| --- a/include/linux/ppp_channel.h | ||||
| +++ b/include/linux/ppp_channel.h | ||||
| @@ -75,6 +75,9 @@ extern int ppp_unit_number(struct ppp_ch | ||||
|  /* Get the device name associated with a channel, or NULL if none */ | ||||
|  extern char *ppp_dev_name(struct ppp_channel *); | ||||
|   | ||||
| +/* Get the socket structure of a given ppp netdev */ | ||||
| +extern struct sock *ppp_netdev_get_sock(struct net_device *dev); | ||||
| + | ||||
|  /* | ||||
|   * SMP locking notes: | ||||
|   * The channel code must ensure that when it calls ppp_unregister_channel, | ||||
| @@ -0,0 +1,45 @@ | ||||
| --- a/drivers/net/vxlan.c | ||||
| +++ b/drivers/net/vxlan.c | ||||
| @@ -23,6 +23,7 @@ | ||||
|  #include <net/rtnetlink.h> | ||||
|  #include <net/inet_ecn.h> | ||||
|  #include <net/net_namespace.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
|  #include <net/netns/generic.h> | ||||
|  #include <net/tun_proto.h> | ||||
|  #include <net/vxlan.h> | ||||
| @@ -2966,6 +2967,24 @@ static int vxlan_fill_metadata_dst(struc | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int vxlan_flow_offload_check(struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct net_device *dev = path->dev; | ||||
| +	struct vxlan_dev *vxlan = netdev_priv(dev); | ||||
| + | ||||
| +	if (path->flags & BIT(DEV_PATH_TNL)) | ||||
| +		return -EEXIST; | ||||
| + | ||||
| +	path->flags |= BIT(DEV_PATH_TNL); | ||||
| +	path->tnl_type = FLOW_OFFLOAD_TNL_VXLAN; | ||||
| +	path->virt_dev = dev; | ||||
| +	path->dev = vxlan->dev; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* CONFIG_NF_FLOW_TABLE */ | ||||
| + | ||||
|  static const struct net_device_ops vxlan_netdev_ether_ops = { | ||||
|  	.ndo_init		= vxlan_init, | ||||
|  	.ndo_uninit		= vxlan_uninit, | ||||
| @@ -2982,6 +3001,9 @@ static const struct net_device_ops vxlan | ||||
|  	.ndo_fdb_dump		= vxlan_fdb_dump, | ||||
|  	.ndo_fdb_get		= vxlan_fdb_get, | ||||
|  	.ndo_fill_metadata_dst	= vxlan_fill_metadata_dst, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.ndo_flow_offload_check = vxlan_flow_offload_check, | ||||
| +#endif | ||||
|  	.ndo_change_proto_down  = dev_change_proto_down_generic, | ||||
|  }; | ||||
|   | ||||
| @@ -0,0 +1,23 @@ | ||||
| --- a/net/l2tp/l2tp_core.c | ||||
| +++ b/net/l2tp/l2tp_core.c | ||||
| @@ -1068,6 +1068,10 @@ int l2tp_xmit_skb(struct l2tp_session *s | ||||
|  	int udp_len; | ||||
|  	int ret = NET_XMIT_SUCCESS; | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	skb_reset_inner_headers(skb); | ||||
| +#endif | ||||
| + | ||||
|  	/* Check that there's enough headroom in the skb to insert IP, | ||||
|  	 * UDP and L2TP headers. If not enough, expand it to | ||||
|  	 * make room. Adjust truesize. | ||||
| --- a/net/l2tp/l2tp_ppp.c | ||||
| +++ b/net/l2tp/l2tp_ppp.c | ||||
| @@ -356,6 +356,7 @@ static int l2tp_ppp_flow_offload_check(s | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	path->flags |= BIT(DEV_PATH_TNL); | ||||
| +	path->tnl_type = FLOW_OFFLOAD_TNL_L2TP_V2; | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -0,0 +1,100 @@ | ||||
| --- a/drivers/net/ppp/pptp.c | ||||
| +++ b/drivers/net/ppp/pptp.c | ||||
| @@ -33,6 +33,7 @@ | ||||
|  #include <net/route.h> | ||||
|  #include <net/gre.h> | ||||
|  #include <net/pptp.h> | ||||
| +#include <net/netfilter/nf_flow_table.h> | ||||
|   | ||||
|  #include <linux/uaccess.h> | ||||
|   | ||||
| @@ -40,6 +41,9 @@ | ||||
|   | ||||
|  #define MAX_CALLID 65535 | ||||
|   | ||||
| +int (*mtk_pptp_seq_next)(u16 call_id, u32 *val) = NULL; | ||||
| +EXPORT_SYMBOL(mtk_pptp_seq_next); | ||||
| + | ||||
|  static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1); | ||||
|  static struct pppox_sock __rcu **callid_sock; | ||||
|   | ||||
| @@ -128,6 +132,26 @@ static void del_chan(struct pppox_sock * | ||||
|  	spin_unlock(&chan_lock); | ||||
|  } | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +static int pptp_flow_offload_check(struct ppp_channel *chan, | ||||
| +				   struct flow_offload_hw_path *path) | ||||
| +{ | ||||
| +	struct sock *sk = (struct sock *)chan->private; | ||||
| +	struct pppox_sock *po = pppox_sk(sk); | ||||
| + | ||||
| +	if (path->flags & BIT(DEV_PATH_TNL)) | ||||
| +		return -EEXIST; | ||||
| + | ||||
| +	if (sk_pppox(po)->sk_state & PPPOX_DEAD) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	path->flags |= BIT(DEV_PATH_TNL); | ||||
| +	path->tnl_type = FLOW_OFFLOAD_TNL_PPTP; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| +#endif /* IS_ENABLED(CONFIG_NF_FLOW_TABLE) */ | ||||
| + | ||||
|  static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | ||||
|  { | ||||
|  	struct sock *sk = (struct sock *) chan->private; | ||||
| @@ -140,6 +164,7 @@ static int pptp_xmit(struct ppp_channel | ||||
|  	int islcp; | ||||
|  	int len; | ||||
|  	unsigned char *data; | ||||
| +	u32 seq_sent_hw; | ||||
|  	__u32 seq_recv; | ||||
|   | ||||
|   | ||||
| @@ -204,7 +229,14 @@ static int pptp_xmit(struct ppp_channel | ||||
|  	hdr->gre_hd.protocol = GRE_PROTO_PPP; | ||||
|  	hdr->call_id = htons(opt->dst_addr.call_id); | ||||
|   | ||||
| -	hdr->seq = htonl(++opt->seq_sent); | ||||
| +	if (mtk_pptp_seq_next && !mtk_pptp_seq_next(opt->dst_addr.call_id, | ||||
| +						    &seq_sent_hw)) { | ||||
| +		opt->seq_sent = seq_sent_hw; | ||||
| +		hdr->seq = htonl(opt->seq_sent); | ||||
| +	} else { | ||||
| +		hdr->seq = htonl(++opt->seq_sent); | ||||
| +	} | ||||
| + | ||||
|  	if (opt->ack_sent != seq_recv)	{ | ||||
|  		/* send ack with this message */ | ||||
|  		hdr->gre_hd.flags |= GRE_ACK; | ||||
| @@ -598,6 +630,9 @@ static int pptp_ppp_ioctl(struct ppp_cha | ||||
|  static const struct ppp_channel_ops pptp_chan_ops = { | ||||
|  	.start_xmit = pptp_xmit, | ||||
|  	.ioctl      = pptp_ppp_ioctl, | ||||
| +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| +	.flow_offload_check = pptp_flow_offload_check, | ||||
| +#endif /* IS_ENABLED(CONFIG_NF_FLOW_TABLE) */ | ||||
|  }; | ||||
|   | ||||
|  static struct proto pptp_sk_proto __read_mostly = { | ||||
| --- a/include/net/pptp.h | ||||
| +++ b/include/net/pptp.h | ||||
| @@ -2,6 +2,8 @@ | ||||
|  #ifndef _NET_PPTP_H | ||||
|  #define _NET_PPTP_H | ||||
|   | ||||
| +#include <net/gre.h> | ||||
| + | ||||
|  #define PPP_LCP_ECHOREQ 0x09 | ||||
|  #define PPP_LCP_ECHOREP 0x0A | ||||
|  #define SC_RCV_BITS     (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) | ||||
| @@ -20,5 +22,7 @@ struct pptp_gre_header { | ||||
|  	__be32 ack; | ||||
|  } __packed; | ||||
|   | ||||
| +/* symbol exported from linux kernel driver/net/ppp/pptp.c */ | ||||
| +extern int (*mtk_pptp_seq_next)(uint16_t call_id, uint32_t *val); | ||||
|   | ||||
|  #endif | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Arif Alam
					Arif Alam