[3/4] net: wan: fsl_qmc_hdlc: Add runtime timeslots changes support

Message ID 20240123164912.249540-4-herve.codina@bootlin.com
State New
Headers
Series [1/4] net: wan: Add support for QMC HDLC |

Commit Message

Herve Codina Jan. 23, 2024, 4:49 p.m. UTC
  QMC channels support runtime timeslots changes but nothing is done at
the QMC HDLC driver to handle these changes.

Use existing IFACE ioctl in order to configure the timeslots to use.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Acked-by: Jakub Kicinski <kuba@kernel.org>
---
 drivers/net/wan/fsl_qmc_hdlc.c | 169 ++++++++++++++++++++++++++++++++-
 1 file changed, 168 insertions(+), 1 deletion(-)
  

Comments

Vadim Fedorenko Jan. 24, 2024, 10:10 a.m. UTC | #1
On 23/01/2024 16:49, Herve Codina wrote:
> QMC channels support runtime timeslots changes but nothing is done at
> the QMC HDLC driver to handle these changes.
> 
> Use existing IFACE ioctl in order to configure the timeslots to use.
> 
> Signed-off-by: Herve Codina <herve.codina@bootlin.com>
> Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu>
> Acked-by: Jakub Kicinski <kuba@kernel.org>
> ---
>   drivers/net/wan/fsl_qmc_hdlc.c | 169 ++++++++++++++++++++++++++++++++-
>   1 file changed, 168 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wan/fsl_qmc_hdlc.c b/drivers/net/wan/fsl_qmc_hdlc.c
> index 31b637ec8390..82019cd96365 100644
> --- a/drivers/net/wan/fsl_qmc_hdlc.c
> +++ b/drivers/net/wan/fsl_qmc_hdlc.c
> @@ -32,6 +32,7 @@ struct qmc_hdlc {
>   	struct qmc_hdlc_desc tx_descs[8];
>   	unsigned int tx_out;
>   	struct qmc_hdlc_desc rx_descs[4];
> +	u32 slot_map;
>   };
>   
>   static inline struct qmc_hdlc *netdev_to_qmc_hdlc(struct net_device *netdev)
> @@ -202,6 +203,162 @@ static netdev_tx_t qmc_hdlc_xmit(struct sk_buff *skb, struct net_device *netdev)
>   	return NETDEV_TX_OK;
>   }
>   
> +static int qmc_hdlc_xlate_slot_map(struct qmc_hdlc *qmc_hdlc,
> +				   u32 slot_map, struct qmc_chan_ts_info *ts_info)
> +{
> +	u64 ts_mask_avail;
> +	unsigned int bit;
> +	unsigned int i;
> +	u64 ts_mask;
> +	u64 map;
> +
> +	/* Tx and Rx masks must be identical */
> +	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
> +		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
> +			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
> +		return -EINVAL;
> +	}
> +
> +	ts_mask_avail = ts_info->rx_ts_mask_avail;
> +	ts_mask = 0;
> +	map = slot_map;
> +	bit = 0;
> +	for (i = 0; i < 64; i++) {
> +		if (ts_mask_avail & BIT_ULL(i)) {
> +			if (map & BIT_ULL(bit))
> +				ts_mask |= BIT_ULL(i);
> +			bit++;
> +		}
> +	}
> +
> +	if (hweight64(ts_mask) != hweight64(map)) {
> +		dev_err(qmc_hdlc->dev, "Cannot translate timeslots 0x%llx -> (0x%llx,0x%llx)\n",
> +			map, ts_mask_avail, ts_mask);
> +		return -EINVAL;
> +	}
> +
> +	ts_info->tx_ts_mask = ts_mask;
> +	ts_info->rx_ts_mask = ts_mask;
> +	return 0;
> +}
> +
> +static int qmc_hdlc_xlate_ts_info(struct qmc_hdlc *qmc_hdlc,
> +				  const struct qmc_chan_ts_info *ts_info, u32 *slot_map)
> +{
> +	u64 ts_mask_avail;
> +	unsigned int bit;
> +	unsigned int i;
> +	u64 ts_mask;
> +	u64 map;
> +

Starting from here ...

> +	/* Tx and Rx masks must be identical */
> +	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
> +		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
> +			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
> +		return -EINVAL;
> +	}
> +	if (ts_info->rx_ts_mask != ts_info->tx_ts_mask) {
> +		dev_err(qmc_hdlc->dev, "tx and rx timeslots mismatch (0x%llx, 0x%llx)\n",
> +			ts_info->rx_ts_mask, ts_info->tx_ts_mask);
> +		return -EINVAL;
> +	}
> +
> +	ts_mask_avail = ts_info->rx_ts_mask_avail;
> +	ts_mask = ts_info->rx_ts_mask;
> +	map = 0;
> +	bit = 0;
> +	for (i = 0; i < 64; i++) {
> +		if (ts_mask_avail & BIT_ULL(i)) {
> +			if (ts_mask & BIT_ULL(i))
> +				map |= BIT_ULL(bit);
> +			bit++;
> +		}
> +	}
> +
> +	if (hweight64(ts_mask) != hweight64(map)) {
> +		dev_err(qmc_hdlc->dev, "Cannot translate timeslots (0x%llx,0x%llx) -> 0x%llx\n",
> +			ts_mask_avail, ts_mask, map);
> +		return -EINVAL;
> +	}
> +

till here the block looks like copy of the block from previous function.
It worth to make a separate function for it, I think.

> +	if (map >= BIT_ULL(32)) {
> +		dev_err(qmc_hdlc->dev, "Slot map out of 32bit (0x%llx,0x%llx) -> 0x%llx\n",
> +			ts_mask_avail, ts_mask, map);
> +		return -EINVAL;
> +	}
> +
> +	*slot_map = map;
> +	return 0;
> +}
> +
> +static int qmc_hdlc_set_iface(struct qmc_hdlc *qmc_hdlc, int if_iface, const te1_settings *te1)
> +{
> +	struct qmc_chan_ts_info ts_info;
> +	int ret;
> +
> +	ret = qmc_chan_get_ts_info(qmc_hdlc->qmc_chan, &ts_info);
> +	if (ret) {
> +		dev_err(qmc_hdlc->dev, "get QMC channel ts info failed %d\n", ret);
> +		return ret;
> +	}
> +	ret = qmc_hdlc_xlate_slot_map(qmc_hdlc, te1->slot_map, &ts_info);
> +	if (ret)
> +		return ret;
> +
> +	ret = qmc_chan_set_ts_info(qmc_hdlc->qmc_chan, &ts_info);
> +	if (ret) {
> +		dev_err(qmc_hdlc->dev, "set QMC channel ts info failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	qmc_hdlc->slot_map = te1->slot_map;
> +
> +	return 0;
> +}
> +
> +static int qmc_hdlc_ioctl(struct net_device *netdev, struct if_settings *ifs)
> +{
> +	struct qmc_hdlc *qmc_hdlc = netdev_to_qmc_hdlc(netdev);
> +	te1_settings te1;
> +
> +	switch (ifs->type) {
> +	case IF_GET_IFACE:
> +		ifs->type = IF_IFACE_E1;
> +		if (ifs->size < sizeof(te1)) {
> +			if (!ifs->size)
> +				return 0; /* only type requested */
> +
> +			ifs->size = sizeof(te1); /* data size wanted */
> +			return -ENOBUFS;
> +		}
> +
> +		memset(&te1, 0, sizeof(te1));
> +
> +		/* Update slot_map */
> +		te1.slot_map = qmc_hdlc->slot_map;
> +
> +		if (copy_to_user(ifs->ifs_ifsu.te1, &te1, sizeof(te1)))
> +			return -EFAULT;
> +		return 0;
> +
> +	case IF_IFACE_E1:
> +	case IF_IFACE_T1:
> +		if (!capable(CAP_NET_ADMIN))
> +			return -EPERM;
> +
> +		if (netdev->flags & IFF_UP)
> +			return -EBUSY;
> +
> +		if (copy_from_user(&te1, ifs->ifs_ifsu.te1, sizeof(te1)))
> +			return -EFAULT;
> +
> +		return qmc_hdlc_set_iface(qmc_hdlc, ifs->type, &te1);
> +
> +	default:
> +		return hdlc_ioctl(netdev, ifs);
> +	}
> +}
> +
>   static int qmc_hdlc_open(struct net_device *netdev)
>   {
>   	struct qmc_hdlc *qmc_hdlc = netdev_to_qmc_hdlc(netdev);
> @@ -328,13 +485,14 @@ static const struct net_device_ops qmc_hdlc_netdev_ops = {
>   	.ndo_open       = qmc_hdlc_open,
>   	.ndo_stop       = qmc_hdlc_close,
>   	.ndo_start_xmit = hdlc_start_xmit,
> -	.ndo_siocwandev	= hdlc_ioctl,
> +	.ndo_siocwandev = qmc_hdlc_ioctl,
>   };
>   
>   static int qmc_hdlc_probe(struct platform_device *pdev)
>   {
>   	struct device_node *np = pdev->dev.of_node;
>   	struct qmc_hdlc *qmc_hdlc;
> +	struct qmc_chan_ts_info ts_info;
>   	struct qmc_chan_info info;
>   	hdlc_device *hdlc;
>   	int ret;
> @@ -364,6 +522,15 @@ static int qmc_hdlc_probe(struct platform_device *pdev)
>   		return -EINVAL;
>   	}
>   
> +	ret = qmc_chan_get_ts_info(qmc_hdlc->qmc_chan, &ts_info);
> +	if (ret) {
> +		dev_err(qmc_hdlc->dev, "get QMC channel ts info failed %d\n", ret);
> +		return ret;
> +	}
> +	ret = qmc_hdlc_xlate_ts_info(qmc_hdlc, &ts_info, &qmc_hdlc->slot_map);
> +	if (ret)
> +		return ret;
> +
>   	qmc_hdlc->netdev = alloc_hdlcdev(qmc_hdlc);
>   	if (!qmc_hdlc->netdev) {
>   		dev_err(qmc_hdlc->dev, "failed to alloc hdlc dev\n");
  
Herve Codina Jan. 24, 2024, 3:26 p.m. UTC | #2
Hi Vadim,

On Wed, 24 Jan 2024 10:10:46 +0000
Vadim Fedorenko <vadim.fedorenko@linux.dev> wrote:

[...]
> > +static int qmc_hdlc_xlate_slot_map(struct qmc_hdlc *qmc_hdlc,
> > +				   u32 slot_map, struct qmc_chan_ts_info *ts_info)
> > +{
> > +	u64 ts_mask_avail;
> > +	unsigned int bit;
> > +	unsigned int i;
> > +	u64 ts_mask;
> > +	u64 map;
> > +
> > +	/* Tx and Rx masks must be identical */
> > +	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
> > +		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
> > +			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ts_mask_avail = ts_info->rx_ts_mask_avail;
> > +	ts_mask = 0;
> > +	map = slot_map;
> > +	bit = 0;
> > +	for (i = 0; i < 64; i++) {
> > +		if (ts_mask_avail & BIT_ULL(i)) {
> > +			if (map & BIT_ULL(bit))
> > +				ts_mask |= BIT_ULL(i);
> > +			bit++;
> > +		}
> > +	}
> > +
> > +	if (hweight64(ts_mask) != hweight64(map)) {
> > +		dev_err(qmc_hdlc->dev, "Cannot translate timeslots 0x%llx -> (0x%llx,0x%llx)\n",
> > +			map, ts_mask_avail, ts_mask);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ts_info->tx_ts_mask = ts_mask;
> > +	ts_info->rx_ts_mask = ts_mask;
> > +	return 0;
> > +}
> > +
> > +static int qmc_hdlc_xlate_ts_info(struct qmc_hdlc *qmc_hdlc,
> > +				  const struct qmc_chan_ts_info *ts_info, u32 *slot_map)
> > +{
> > +	u64 ts_mask_avail;
> > +	unsigned int bit;
> > +	unsigned int i;
> > +	u64 ts_mask;
> > +	u64 map;
> > +  
> 
> Starting from here ...
> 
> > +	/* Tx and Rx masks must be identical */
> > +	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
> > +		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
> > +			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
> > +		return -EINVAL;
> > +	}
> > +	if (ts_info->rx_ts_mask != ts_info->tx_ts_mask) {
> > +		dev_err(qmc_hdlc->dev, "tx and rx timeslots mismatch (0x%llx, 0x%llx)\n",
> > +			ts_info->rx_ts_mask, ts_info->tx_ts_mask);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ts_mask_avail = ts_info->rx_ts_mask_avail;
> > +	ts_mask = ts_info->rx_ts_mask;
> > +	map = 0;
> > +	bit = 0;
> > +	for (i = 0; i < 64; i++) {
> > +		if (ts_mask_avail & BIT_ULL(i)) {
> > +			if (ts_mask & BIT_ULL(i))
> > +				map |= BIT_ULL(bit);
> > +			bit++;
> > +		}
> > +	}
> > +
> > +	if (hweight64(ts_mask) != hweight64(map)) {
> > +		dev_err(qmc_hdlc->dev, "Cannot translate timeslots (0x%llx,0x%llx) -> 0x%llx\n",
> > +			ts_mask_avail, ts_mask, map);
> > +		return -EINVAL;
> > +	}
> > +  
> 
> till here the block looks like copy of the block from previous function.
> It worth to make a separate function for it, I think.
> 
> > +	if (map >= BIT_ULL(32)) {
> > +		dev_err(qmc_hdlc->dev, "Slot map out of 32bit (0x%llx,0x%llx) -> 0x%llx\n",
> > +			ts_mask_avail, ts_mask, map);
> > +		return -EINVAL;
> > +	}
> > +
> > +	*slot_map = map;
> > +	return 0;
> > +}
> > +
[...]

I am not so sure. There are slighty differences between the two functions.
The error messages and, in particular, the loop in qmc_hdlc_xlate_slot_map() is:
	--- 8< ---
	ts_mask_avail = ts_info->rx_ts_mask_avail;
	ts_mask = 0;
	map = slot_map;
	bit = 0;
	for (i = 0; i < 64; i++) {
		if (ts_mask_avail & BIT_ULL(i)) {
			if (map & BIT_ULL(bit))
				ts_mask |= BIT_ULL(i);
			bit++;
		}
	}
	--- 8< ---

whereas it is the following in qmc_hdlc_xlate_ts_info():
	--- 8< ---
	ts_mask_avail = ts_info->rx_ts_mask_avail;
	ts_mask = ts_info->rx_ts_mask;
	map = 0;
	bit = 0;
	for (i = 0; i < 64; i++) {
		if (ts_mask_avail & BIT_ULL(i)) {
			if (ts_mask & BIT_ULL(i))
				map |= BIT_ULL(bit);
			bit++;
		}
	}
	--- 8< ---

ts_map and map initializations are not the same, i and bit are not used for
the same purpose and the computed value is not computed based on the same
information.

With that pointed, I am not sure that having some common code for both
function will be relevant. Your opinion ?

Best regards,
Hervé
  
Vadim Fedorenko Jan. 24, 2024, 4:19 p.m. UTC | #3
On 24/01/2024 15:26, Herve Codina wrote:
> Hi Vadim,
> 
> On Wed, 24 Jan 2024 10:10:46 +0000
> Vadim Fedorenko <vadim.fedorenko@linux.dev> wrote:
> 
> [...]
>>> +static int qmc_hdlc_xlate_slot_map(struct qmc_hdlc *qmc_hdlc,
>>> +				   u32 slot_map, struct qmc_chan_ts_info *ts_info)
>>> +{
>>> +	u64 ts_mask_avail;
>>> +	unsigned int bit;
>>> +	unsigned int i;
>>> +	u64 ts_mask;
>>> +	u64 map;
>>> +
>>> +	/* Tx and Rx masks must be identical */
>>> +	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
>>> +		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
>>> +			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	ts_mask_avail = ts_info->rx_ts_mask_avail;
>>> +	ts_mask = 0;
>>> +	map = slot_map;
>>> +	bit = 0;
>>> +	for (i = 0; i < 64; i++) {
>>> +		if (ts_mask_avail & BIT_ULL(i)) {
>>> +			if (map & BIT_ULL(bit))
>>> +				ts_mask |= BIT_ULL(i);
>>> +			bit++;
>>> +		}
>>> +	}
>>> +
>>> +	if (hweight64(ts_mask) != hweight64(map)) {
>>> +		dev_err(qmc_hdlc->dev, "Cannot translate timeslots 0x%llx -> (0x%llx,0x%llx)\n",
>>> +			map, ts_mask_avail, ts_mask);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	ts_info->tx_ts_mask = ts_mask;
>>> +	ts_info->rx_ts_mask = ts_mask;
>>> +	return 0;
>>> +}
>>> +
>>> +static int qmc_hdlc_xlate_ts_info(struct qmc_hdlc *qmc_hdlc,
>>> +				  const struct qmc_chan_ts_info *ts_info, u32 *slot_map)
>>> +{
>>> +	u64 ts_mask_avail;
>>> +	unsigned int bit;
>>> +	unsigned int i;
>>> +	u64 ts_mask;
>>> +	u64 map;
>>> +
>>
>> Starting from here ...
>>
>>> +	/* Tx and Rx masks must be identical */
>>> +	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
>>> +		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
>>> +			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ts_info->rx_ts_mask != ts_info->tx_ts_mask) {
>>> +		dev_err(qmc_hdlc->dev, "tx and rx timeslots mismatch (0x%llx, 0x%llx)\n",
>>> +			ts_info->rx_ts_mask, ts_info->tx_ts_mask);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	ts_mask_avail = ts_info->rx_ts_mask_avail;
>>> +	ts_mask = ts_info->rx_ts_mask;
>>> +	map = 0;
>>> +	bit = 0;
>>> +	for (i = 0; i < 64; i++) {
>>> +		if (ts_mask_avail & BIT_ULL(i)) {
>>> +			if (ts_mask & BIT_ULL(i))
>>> +				map |= BIT_ULL(bit);
>>> +			bit++;
>>> +		}
>>> +	}
>>> +
>>> +	if (hweight64(ts_mask) != hweight64(map)) {
>>> +		dev_err(qmc_hdlc->dev, "Cannot translate timeslots (0x%llx,0x%llx) -> 0x%llx\n",
>>> +			ts_mask_avail, ts_mask, map);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>
>> till here the block looks like copy of the block from previous function.
>> It worth to make a separate function for it, I think.
>>
>>> +	if (map >= BIT_ULL(32)) {
>>> +		dev_err(qmc_hdlc->dev, "Slot map out of 32bit (0x%llx,0x%llx) -> 0x%llx\n",
>>> +			ts_mask_avail, ts_mask, map);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	*slot_map = map;
>>> +	return 0;
>>> +}
>>> +
> [...]
> 
> I am not so sure. There are slighty differences between the two functions.
> The error messages and, in particular, the loop in qmc_hdlc_xlate_slot_map() is:
> 	--- 8< ---
> 	ts_mask_avail = ts_info->rx_ts_mask_avail;
> 	ts_mask = 0;
> 	map = slot_map;
> 	bit = 0;
> 	for (i = 0; i < 64; i++) {
> 		if (ts_mask_avail & BIT_ULL(i)) {
> 			if (map & BIT_ULL(bit))
> 				ts_mask |= BIT_ULL(i);
> 			bit++;
> 		}
> 	}
> 	--- 8< ---
> 
> whereas it is the following in qmc_hdlc_xlate_ts_info():
> 	--- 8< ---
> 	ts_mask_avail = ts_info->rx_ts_mask_avail;
> 	ts_mask = ts_info->rx_ts_mask;
> 	map = 0;
> 	bit = 0;
> 	for (i = 0; i < 64; i++) {
> 		if (ts_mask_avail & BIT_ULL(i)) {
> 			if (ts_mask & BIT_ULL(i))
> 				map |= BIT_ULL(bit);
> 			bit++;
> 		}
> 	}
> 	--- 8< ---
> 
> ts_map and map initializations are not the same, i and bit are not used for
> the same purpose and the computed value is not computed based on the same
> information.
> 
> With that pointed, I am not sure that having some common code for both
> function will be relevant. Your opinion ?

I see. I'm just thinking if it's possible to use helpers from bitops.h
and bitmap.h here to avoid open-coding common parts of the code.

> Best regards,
> Hervé
  

Patch

diff --git a/drivers/net/wan/fsl_qmc_hdlc.c b/drivers/net/wan/fsl_qmc_hdlc.c
index 31b637ec8390..82019cd96365 100644
--- a/drivers/net/wan/fsl_qmc_hdlc.c
+++ b/drivers/net/wan/fsl_qmc_hdlc.c
@@ -32,6 +32,7 @@  struct qmc_hdlc {
 	struct qmc_hdlc_desc tx_descs[8];
 	unsigned int tx_out;
 	struct qmc_hdlc_desc rx_descs[4];
+	u32 slot_map;
 };
 
 static inline struct qmc_hdlc *netdev_to_qmc_hdlc(struct net_device *netdev)
@@ -202,6 +203,162 @@  static netdev_tx_t qmc_hdlc_xmit(struct sk_buff *skb, struct net_device *netdev)
 	return NETDEV_TX_OK;
 }
 
+static int qmc_hdlc_xlate_slot_map(struct qmc_hdlc *qmc_hdlc,
+				   u32 slot_map, struct qmc_chan_ts_info *ts_info)
+{
+	u64 ts_mask_avail;
+	unsigned int bit;
+	unsigned int i;
+	u64 ts_mask;
+	u64 map;
+
+	/* Tx and Rx masks must be identical */
+	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
+		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
+			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
+		return -EINVAL;
+	}
+
+	ts_mask_avail = ts_info->rx_ts_mask_avail;
+	ts_mask = 0;
+	map = slot_map;
+	bit = 0;
+	for (i = 0; i < 64; i++) {
+		if (ts_mask_avail & BIT_ULL(i)) {
+			if (map & BIT_ULL(bit))
+				ts_mask |= BIT_ULL(i);
+			bit++;
+		}
+	}
+
+	if (hweight64(ts_mask) != hweight64(map)) {
+		dev_err(qmc_hdlc->dev, "Cannot translate timeslots 0x%llx -> (0x%llx,0x%llx)\n",
+			map, ts_mask_avail, ts_mask);
+		return -EINVAL;
+	}
+
+	ts_info->tx_ts_mask = ts_mask;
+	ts_info->rx_ts_mask = ts_mask;
+	return 0;
+}
+
+static int qmc_hdlc_xlate_ts_info(struct qmc_hdlc *qmc_hdlc,
+				  const struct qmc_chan_ts_info *ts_info, u32 *slot_map)
+{
+	u64 ts_mask_avail;
+	unsigned int bit;
+	unsigned int i;
+	u64 ts_mask;
+	u64 map;
+
+	/* Tx and Rx masks must be identical */
+	if (ts_info->rx_ts_mask_avail != ts_info->tx_ts_mask_avail) {
+		dev_err(qmc_hdlc->dev, "tx and rx available timeslots mismatch (0x%llx, 0x%llx)\n",
+			ts_info->rx_ts_mask_avail, ts_info->tx_ts_mask_avail);
+		return -EINVAL;
+	}
+	if (ts_info->rx_ts_mask != ts_info->tx_ts_mask) {
+		dev_err(qmc_hdlc->dev, "tx and rx timeslots mismatch (0x%llx, 0x%llx)\n",
+			ts_info->rx_ts_mask, ts_info->tx_ts_mask);
+		return -EINVAL;
+	}
+
+	ts_mask_avail = ts_info->rx_ts_mask_avail;
+	ts_mask = ts_info->rx_ts_mask;
+	map = 0;
+	bit = 0;
+	for (i = 0; i < 64; i++) {
+		if (ts_mask_avail & BIT_ULL(i)) {
+			if (ts_mask & BIT_ULL(i))
+				map |= BIT_ULL(bit);
+			bit++;
+		}
+	}
+
+	if (hweight64(ts_mask) != hweight64(map)) {
+		dev_err(qmc_hdlc->dev, "Cannot translate timeslots (0x%llx,0x%llx) -> 0x%llx\n",
+			ts_mask_avail, ts_mask, map);
+		return -EINVAL;
+	}
+
+	if (map >= BIT_ULL(32)) {
+		dev_err(qmc_hdlc->dev, "Slot map out of 32bit (0x%llx,0x%llx) -> 0x%llx\n",
+			ts_mask_avail, ts_mask, map);
+		return -EINVAL;
+	}
+
+	*slot_map = map;
+	return 0;
+}
+
+static int qmc_hdlc_set_iface(struct qmc_hdlc *qmc_hdlc, int if_iface, const te1_settings *te1)
+{
+	struct qmc_chan_ts_info ts_info;
+	int ret;
+
+	ret = qmc_chan_get_ts_info(qmc_hdlc->qmc_chan, &ts_info);
+	if (ret) {
+		dev_err(qmc_hdlc->dev, "get QMC channel ts info failed %d\n", ret);
+		return ret;
+	}
+	ret = qmc_hdlc_xlate_slot_map(qmc_hdlc, te1->slot_map, &ts_info);
+	if (ret)
+		return ret;
+
+	ret = qmc_chan_set_ts_info(qmc_hdlc->qmc_chan, &ts_info);
+	if (ret) {
+		dev_err(qmc_hdlc->dev, "set QMC channel ts info failed %d\n", ret);
+		return ret;
+	}
+
+	qmc_hdlc->slot_map = te1->slot_map;
+
+	return 0;
+}
+
+static int qmc_hdlc_ioctl(struct net_device *netdev, struct if_settings *ifs)
+{
+	struct qmc_hdlc *qmc_hdlc = netdev_to_qmc_hdlc(netdev);
+	te1_settings te1;
+
+	switch (ifs->type) {
+	case IF_GET_IFACE:
+		ifs->type = IF_IFACE_E1;
+		if (ifs->size < sizeof(te1)) {
+			if (!ifs->size)
+				return 0; /* only type requested */
+
+			ifs->size = sizeof(te1); /* data size wanted */
+			return -ENOBUFS;
+		}
+
+		memset(&te1, 0, sizeof(te1));
+
+		/* Update slot_map */
+		te1.slot_map = qmc_hdlc->slot_map;
+
+		if (copy_to_user(ifs->ifs_ifsu.te1, &te1, sizeof(te1)))
+			return -EFAULT;
+		return 0;
+
+	case IF_IFACE_E1:
+	case IF_IFACE_T1:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (netdev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&te1, ifs->ifs_ifsu.te1, sizeof(te1)))
+			return -EFAULT;
+
+		return qmc_hdlc_set_iface(qmc_hdlc, ifs->type, &te1);
+
+	default:
+		return hdlc_ioctl(netdev, ifs);
+	}
+}
+
 static int qmc_hdlc_open(struct net_device *netdev)
 {
 	struct qmc_hdlc *qmc_hdlc = netdev_to_qmc_hdlc(netdev);
@@ -328,13 +485,14 @@  static const struct net_device_ops qmc_hdlc_netdev_ops = {
 	.ndo_open       = qmc_hdlc_open,
 	.ndo_stop       = qmc_hdlc_close,
 	.ndo_start_xmit = hdlc_start_xmit,
-	.ndo_siocwandev	= hdlc_ioctl,
+	.ndo_siocwandev = qmc_hdlc_ioctl,
 };
 
 static int qmc_hdlc_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
 	struct qmc_hdlc *qmc_hdlc;
+	struct qmc_chan_ts_info ts_info;
 	struct qmc_chan_info info;
 	hdlc_device *hdlc;
 	int ret;
@@ -364,6 +522,15 @@  static int qmc_hdlc_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
+	ret = qmc_chan_get_ts_info(qmc_hdlc->qmc_chan, &ts_info);
+	if (ret) {
+		dev_err(qmc_hdlc->dev, "get QMC channel ts info failed %d\n", ret);
+		return ret;
+	}
+	ret = qmc_hdlc_xlate_ts_info(qmc_hdlc, &ts_info, &qmc_hdlc->slot_map);
+	if (ret)
+		return ret;
+
 	qmc_hdlc->netdev = alloc_hdlcdev(qmc_hdlc);
 	if (!qmc_hdlc->netdev) {
 		dev_err(qmc_hdlc->dev, "failed to alloc hdlc dev\n");