* the egress timestamps.
         */
        int ptpegr_ts_bytes;
+       int num_cbs_shapers;
        const struct sja1105_dynamic_table_ops *dyn_ops;
        const struct sja1105_table_ops *static_ops;
        const struct sja1105_regs *regs;
        struct mutex mgmt_lock;
        bool expect_dsa_8021q;
        enum sja1105_vlan_state vlan_state;
+       struct sja1105_cbs_entry *cbs;
        struct sja1105_tagger_data tagger_data;
        struct sja1105_ptp_data ptp_data;
        struct sja1105_tas_data tas_data;
 
 #define SJA1105_SIZE_RETAGGING_DYN_CMD                         \
        (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_RETAGGING_ENTRY)
 
+#define SJA1105ET_SIZE_CBS_DYN_CMD                             \
+       (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_CBS_ENTRY)
+
+#define SJA1105PQRS_SIZE_CBS_DYN_CMD                           \
+       (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_CBS_ENTRY)
+
 #define SJA1105_MAX_DYN_CMD_SIZE                               \
        SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD
 
        sja1105_packing(p, &cmd->index,     5,  0, size, op);
 }
 
+static void sja1105et_cbs_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
+                                     enum packing_op op)
+{
+       u8 *p = buf + SJA1105ET_SIZE_CBS_ENTRY;
+       const int size = SJA1105_SIZE_DYN_CMD;
+
+       sja1105_packing(p, &cmd->valid, 31, 31, size, op);
+       sja1105_packing(p, &cmd->index, 19, 16, size, op);
+}
+
+static size_t sja1105et_cbs_entry_packing(void *buf, void *entry_ptr,
+                                         enum packing_op op)
+{
+       const size_t size = SJA1105ET_SIZE_CBS_ENTRY;
+       struct sja1105_cbs_entry *entry = entry_ptr;
+       u8 *cmd = buf + size;
+       u32 *p = buf;
+
+       sja1105_packing(cmd, &entry->port, 5, 3, SJA1105_SIZE_DYN_CMD, op);
+       sja1105_packing(cmd, &entry->prio, 2, 0, SJA1105_SIZE_DYN_CMD, op);
+       sja1105_packing(p + 3, &entry->credit_lo,  31, 0, size, op);
+       sja1105_packing(p + 2, &entry->credit_hi,  31, 0, size, op);
+       sja1105_packing(p + 1, &entry->send_slope, 31, 0, size, op);
+       sja1105_packing(p + 0, &entry->idle_slope, 31, 0, size, op);
+       return size;
+}
+
+static void sja1105pqrs_cbs_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
+                                       enum packing_op op)
+{
+       u8 *p = buf + SJA1105PQRS_SIZE_CBS_ENTRY;
+       const int size = SJA1105_SIZE_DYN_CMD;
+
+       sja1105_packing(p, &cmd->valid,   31, 31, size, op);
+       sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op);
+       sja1105_packing(p, &cmd->errors,  29, 29, size, op);
+       sja1105_packing(p, &cmd->index,    3,  0, size, op);
+}
+
+static size_t sja1105pqrs_cbs_entry_packing(void *buf, void *entry_ptr,
+                                           enum packing_op op)
+{
+       const size_t size = SJA1105PQRS_SIZE_CBS_ENTRY;
+       struct sja1105_cbs_entry *entry = entry_ptr;
+
+       sja1105_packing(buf, &entry->port,      159, 157, size, op);
+       sja1105_packing(buf, &entry->prio,      156, 154, size, op);
+       sja1105_packing(buf, &entry->credit_lo, 153, 122, size, op);
+       sja1105_packing(buf, &entry->credit_hi, 121,  90, size, op);
+       sja1105_packing(buf, &entry->send_slope, 89,  58, size, op);
+       sja1105_packing(buf, &entry->idle_slope, 57,  26, size, op);
+       return size;
+}
+
 #define OP_READ                BIT(0)
 #define OP_WRITE       BIT(1)
 #define OP_DEL         BIT(2)
                .packed_size = SJA1105_SIZE_RETAGGING_DYN_CMD,
                .addr = 0x31,
        },
+       [BLK_IDX_CBS] = {
+               .entry_packing = sja1105et_cbs_entry_packing,
+               .cmd_packing = sja1105et_cbs_cmd_packing,
+               .max_entry_count = SJA1105ET_MAX_CBS_COUNT,
+               .access = OP_WRITE,
+               .packed_size = SJA1105ET_SIZE_CBS_DYN_CMD,
+               .addr = 0x2c,
+       },
        [BLK_IDX_XMII_PARAMS] = {0},
 };
 
                .packed_size = SJA1105_SIZE_RETAGGING_DYN_CMD,
                .addr = 0x38,
        },
+       [BLK_IDX_CBS] = {
+               .entry_packing = sja1105pqrs_cbs_entry_packing,
+               .cmd_packing = sja1105pqrs_cbs_cmd_packing,
+               .max_entry_count = SJA1105PQRS_MAX_CBS_COUNT,
+               .access = OP_WRITE,
+               .packed_size = SJA1105PQRS_SIZE_CBS_DYN_CMD,
+               .addr = 0x32,
+       },
        [BLK_IDX_XMII_PARAMS] = {0},
 };
 
 
        sja1105_bridge_member(ds, port, br, false);
 }
 
+#define BYTES_PER_KBIT (1000LL / 8)
+
+static int sja1105_find_unused_cbs_shaper(struct sja1105_private *priv)
+{
+       int i;
+
+       for (i = 0; i < priv->info->num_cbs_shapers; i++)
+               if (!priv->cbs[i].idle_slope && !priv->cbs[i].send_slope)
+                       return i;
+
+       return -1;
+}
+
+static int sja1105_delete_cbs_shaper(struct sja1105_private *priv, int port,
+                                    int prio)
+{
+       int i;
+
+       for (i = 0; i < priv->info->num_cbs_shapers; i++) {
+               struct sja1105_cbs_entry *cbs = &priv->cbs[i];
+
+               if (cbs->port == port && cbs->prio == prio) {
+                       memset(cbs, 0, sizeof(*cbs));
+                       return sja1105_dynamic_config_write(priv, BLK_IDX_CBS,
+                                                           i, cbs, true);
+               }
+       }
+
+       return 0;
+}
+
+static int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port,
+                               struct tc_cbs_qopt_offload *offload)
+{
+       struct sja1105_private *priv = ds->priv;
+       struct sja1105_cbs_entry *cbs;
+       int index;
+
+       if (!offload->enable)
+               return sja1105_delete_cbs_shaper(priv, port, offload->queue);
+
+       index = sja1105_find_unused_cbs_shaper(priv);
+       if (index < 0)
+               return -ENOSPC;
+
+       cbs = &priv->cbs[index];
+       cbs->port = port;
+       cbs->prio = offload->queue;
+       /* locredit and sendslope are negative by definition. In hardware,
+        * positive values must be provided, and the negative sign is implicit.
+        */
+       cbs->credit_hi = offload->hicredit;
+       cbs->credit_lo = abs(offload->locredit);
+       /* User space is in kbits/sec, hardware in bytes/sec */
+       cbs->idle_slope = offload->idleslope * BYTES_PER_KBIT;
+       cbs->send_slope = abs(offload->sendslope * BYTES_PER_KBIT);
+       /* Convert the negative values from 64-bit 2's complement
+        * to 32-bit 2's complement (for the case of 0x80000000 whose
+        * negative is still negative).
+        */
+       cbs->credit_lo &= GENMASK_ULL(31, 0);
+       cbs->send_slope &= GENMASK_ULL(31, 0);
+
+       return sja1105_dynamic_config_write(priv, BLK_IDX_CBS, index, cbs,
+                                           true);
+}
+
+static int sja1105_reload_cbs(struct sja1105_private *priv)
+{
+       int rc = 0, i;
+
+       for (i = 0; i < priv->info->num_cbs_shapers; i++) {
+               struct sja1105_cbs_entry *cbs = &priv->cbs[i];
+
+               if (!cbs->idle_slope && !cbs->send_slope)
+                       continue;
+
+               rc = sja1105_dynamic_config_write(priv, BLK_IDX_CBS, i, cbs,
+                                                 true);
+               if (rc)
+                       break;
+       }
+
+       return rc;
+}
+
 static const char * const sja1105_reset_reasons[] = {
        [SJA1105_VLAN_FILTERING] = "VLAN filtering",
        [SJA1105_RX_HWTSTAMPING] = "RX timestamping",
                        sja1105_sgmii_pcs_force_speed(priv, speed);
                }
        }
+
+       rc = sja1105_reload_cbs(priv);
+       if (rc < 0)
+               goto out;
 out:
        mutex_unlock(&priv->mgmt_lock);
 
        switch (type) {
        case TC_SETUP_QDISC_TAPRIO:
                return sja1105_setup_tc_taprio(ds, port, type_data);
+       case TC_SETUP_QDISC_CBS:
+               return sja1105_setup_tc_cbs(ds, port, type_data);
        default:
                return -EOPNOTSUPP;
        }
        if (rc)
                return rc;
 
+       if (IS_ENABLED(CONFIG_NET_SCH_CBS)) {
+               priv->cbs = devm_kcalloc(dev, priv->info->num_cbs_shapers,
+                                        sizeof(struct sja1105_cbs_entry),
+                                        GFP_KERNEL);
+               if (!priv->cbs)
+                       return -ENOMEM;
+       }
+
        /* Connections between dsa_port and sja1105_port */
        for (port = 0; port < SJA1105_NUM_PORTS; port++) {
                struct sja1105_port *sp = &priv->ports[port];
 
        .qinq_tpid              = ETH_P_8021Q,
        .ptp_ts_bits            = 24,
        .ptpegr_ts_bytes        = 4,
+       .num_cbs_shapers        = SJA1105ET_MAX_CBS_COUNT,
        .reset_cmd              = sja1105et_reset_cmd,
        .fdb_add_cmd            = sja1105et_fdb_add,
        .fdb_del_cmd            = sja1105et_fdb_del,
        .qinq_tpid              = ETH_P_8021Q,
        .ptp_ts_bits            = 24,
        .ptpegr_ts_bytes        = 4,
+       .num_cbs_shapers        = SJA1105ET_MAX_CBS_COUNT,
        .reset_cmd              = sja1105et_reset_cmd,
        .fdb_add_cmd            = sja1105et_fdb_add,
        .fdb_del_cmd            = sja1105et_fdb_del,
        .qinq_tpid              = ETH_P_8021AD,
        .ptp_ts_bits            = 32,
        .ptpegr_ts_bytes        = 8,
+       .num_cbs_shapers        = SJA1105PQRS_MAX_CBS_COUNT,
        .setup_rgmii_delay      = sja1105pqrs_setup_rgmii_delay,
        .reset_cmd              = sja1105pqrs_reset_cmd,
        .fdb_add_cmd            = sja1105pqrs_fdb_add,
        .qinq_tpid              = ETH_P_8021AD,
        .ptp_ts_bits            = 32,
        .ptpegr_ts_bytes        = 8,
+       .num_cbs_shapers        = SJA1105PQRS_MAX_CBS_COUNT,
        .setup_rgmii_delay      = sja1105pqrs_setup_rgmii_delay,
        .reset_cmd              = sja1105pqrs_reset_cmd,
        .fdb_add_cmd            = sja1105pqrs_fdb_add,
        .qinq_tpid              = ETH_P_8021AD,
        .ptp_ts_bits            = 32,
        .ptpegr_ts_bytes        = 8,
+       .num_cbs_shapers        = SJA1105PQRS_MAX_CBS_COUNT,
        .setup_rgmii_delay      = sja1105pqrs_setup_rgmii_delay,
        .reset_cmd              = sja1105pqrs_reset_cmd,
        .fdb_add_cmd            = sja1105pqrs_fdb_add,
        .qinq_tpid              = ETH_P_8021AD,
        .ptp_ts_bits            = 32,
        .ptpegr_ts_bytes        = 8,
+       .num_cbs_shapers        = SJA1105PQRS_MAX_CBS_COUNT,
        .setup_rgmii_delay      = sja1105pqrs_setup_rgmii_delay,
        .reset_cmd              = sja1105pqrs_reset_cmd,
        .fdb_add_cmd            = sja1105pqrs_fdb_add,
 
 #define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_ENTRY          4
 #define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY            40
 #define SJA1105ET_SIZE_AVB_PARAMS_ENTRY                        12
+#define SJA1105ET_SIZE_CBS_ENTRY                       16
 #define SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY               20
 #define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY              32
 #define SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY                16
 #define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY          44
 #define SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY              16
+#define SJA1105PQRS_SIZE_CBS_ENTRY                     20
 
 /* UM10944.pdf Page 11, Table 2. Configuration Blocks */
 enum {
        BLKID_AVB_PARAMS                                = 0x10,
        BLKID_GENERAL_PARAMS                            = 0x11,
        BLKID_RETAGGING                                 = 0x12,
+       BLKID_CBS                                       = 0x13,
        BLKID_XMII_PARAMS                               = 0x4E,
 };
 
        BLK_IDX_AVB_PARAMS,
        BLK_IDX_GENERAL_PARAMS,
        BLK_IDX_RETAGGING,
+       BLK_IDX_CBS,
        BLK_IDX_XMII_PARAMS,
        BLK_IDX_MAX,
        /* Fake block indices that are only valid for dynamic access */
 #define SJA1105_MAX_RETAGGING_COUNT                    32
 #define SJA1105_MAX_XMII_PARAMS_COUNT                  1
 #define SJA1105_MAX_AVB_PARAMS_COUNT                   1
+#define SJA1105ET_MAX_CBS_COUNT                                10
+#define SJA1105PQRS_MAX_CBS_COUNT                      16
 
 #define SJA1105_MAX_FRAME_MEMORY                       929
 #define SJA1105_MAX_FRAME_MEMORY_RETAGGING             910
        u64 destports;
 };
 
+struct sja1105_cbs_entry {
+       u64 port;
+       u64 prio;
+       u64 credit_hi;
+       u64 credit_lo;
+       u64 send_slope;
+       u64 idle_slope;
+};
+
 struct sja1105_xmii_params_entry {
        u64 phy_mac[5];
        u64 xmii_mode[5];