|
@@ -122,6 +122,7 @@ struct cpdma_chan {
|
|
struct cpdma_chan_stats stats;
|
|
struct cpdma_chan_stats stats;
|
|
/* offsets into dmaregs */
|
|
/* offsets into dmaregs */
|
|
int int_set, int_clear, td;
|
|
int int_set, int_clear, td;
|
|
|
|
+ int weight;
|
|
};
|
|
};
|
|
|
|
|
|
struct cpdma_control_info {
|
|
struct cpdma_control_info {
|
|
@@ -474,29 +475,131 @@ u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(cpdma_ctrl_txchs_state);
|
|
EXPORT_SYMBOL_GPL(cpdma_ctrl_txchs_state);
|
|
|
|
|
|
|
|
+static void cpdma_chan_set_descs(struct cpdma_ctlr *ctlr,
|
|
|
|
+ int rx, int desc_num,
|
|
|
|
+ int per_ch_desc)
|
|
|
|
+{
|
|
|
|
+ struct cpdma_chan *chan, *most_chan = NULL;
|
|
|
|
+ int desc_cnt = desc_num;
|
|
|
|
+ int most_dnum = 0;
|
|
|
|
+ int min, max, i;
|
|
|
|
+
|
|
|
|
+ if (!desc_num)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (rx) {
|
|
|
|
+ min = rx_chan_num(0);
|
|
|
|
+ max = rx_chan_num(CPDMA_MAX_CHANNELS);
|
|
|
|
+ } else {
|
|
|
|
+ min = tx_chan_num(0);
|
|
|
|
+ max = tx_chan_num(CPDMA_MAX_CHANNELS);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (i = min; i < max; i++) {
|
|
|
|
+ chan = ctlr->channels[i];
|
|
|
|
+ if (!chan)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (chan->weight)
|
|
|
|
+ chan->desc_num = (chan->weight * desc_num) / 100;
|
|
|
|
+ else
|
|
|
|
+ chan->desc_num = per_ch_desc;
|
|
|
|
+
|
|
|
|
+ desc_cnt -= chan->desc_num;
|
|
|
|
+
|
|
|
|
+ if (most_dnum < chan->desc_num) {
|
|
|
|
+ most_dnum = chan->desc_num;
|
|
|
|
+ most_chan = chan;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ /* use remains */
|
|
|
|
+ most_chan->desc_num += desc_cnt;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* cpdma_chan_split_pool - Splits ctrl pool between all channels.
|
|
* cpdma_chan_split_pool - Splits ctrl pool between all channels.
|
|
* Has to be called under ctlr lock
|
|
* Has to be called under ctlr lock
|
|
*/
|
|
*/
|
|
-static void cpdma_chan_split_pool(struct cpdma_ctlr *ctlr)
|
|
|
|
|
|
+static int cpdma_chan_split_pool(struct cpdma_ctlr *ctlr)
|
|
{
|
|
{
|
|
|
|
+ int tx_per_ch_desc = 0, rx_per_ch_desc = 0;
|
|
struct cpdma_desc_pool *pool = ctlr->pool;
|
|
struct cpdma_desc_pool *pool = ctlr->pool;
|
|
|
|
+ int free_rx_num = 0, free_tx_num = 0;
|
|
|
|
+ int rx_weight = 0, tx_weight = 0;
|
|
|
|
+ int tx_desc_num, rx_desc_num;
|
|
struct cpdma_chan *chan;
|
|
struct cpdma_chan *chan;
|
|
- int ch_desc_num;
|
|
|
|
- int i;
|
|
|
|
|
|
+ int i, tx_num = 0;
|
|
|
|
|
|
if (!ctlr->chan_num)
|
|
if (!ctlr->chan_num)
|
|
- return;
|
|
|
|
-
|
|
|
|
- /* calculate average size of pool slice */
|
|
|
|
- ch_desc_num = pool->num_desc / ctlr->chan_num;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
- /* split ctlr pool */
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
|
|
for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
|
|
chan = ctlr->channels[i];
|
|
chan = ctlr->channels[i];
|
|
- if (chan)
|
|
|
|
- chan->desc_num = ch_desc_num;
|
|
|
|
|
|
+ if (!chan)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (is_rx_chan(chan)) {
|
|
|
|
+ if (!chan->weight)
|
|
|
|
+ free_rx_num++;
|
|
|
|
+ rx_weight += chan->weight;
|
|
|
|
+ } else {
|
|
|
|
+ if (!chan->weight)
|
|
|
|
+ free_tx_num++;
|
|
|
|
+ tx_weight += chan->weight;
|
|
|
|
+ tx_num++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rx_weight > 100 || tx_weight > 100)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ tx_desc_num = (tx_num * pool->num_desc) / ctlr->chan_num;
|
|
|
|
+ rx_desc_num = pool->num_desc - tx_desc_num;
|
|
|
|
+
|
|
|
|
+ if (free_tx_num) {
|
|
|
|
+ tx_per_ch_desc = tx_desc_num - (tx_weight * tx_desc_num) / 100;
|
|
|
|
+ tx_per_ch_desc /= free_tx_num;
|
|
|
|
+ }
|
|
|
|
+ if (free_rx_num) {
|
|
|
|
+ rx_per_ch_desc = rx_desc_num - (rx_weight * rx_desc_num) / 100;
|
|
|
|
+ rx_per_ch_desc /= free_rx_num;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ cpdma_chan_set_descs(ctlr, 0, tx_desc_num, tx_per_ch_desc);
|
|
|
|
+ cpdma_chan_set_descs(ctlr, 1, rx_desc_num, rx_per_ch_desc);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* cpdma_chan_set_weight - set weight of a channel in percentage.
|
|
|
|
+ * Tx and Rx channels have separate weights. That is 100% for RX
|
|
|
|
+ * and 100% for Tx. The weight is used to split cpdma resources
|
|
|
|
+ * in correct proportion required by the channels, including number
|
|
|
|
+ * of descriptors. The channel rate is not enough to know the
|
|
|
|
+ * weight of a channel as the maximum rate of an interface is needed.
|
|
|
|
+ * If weight = 0, then channel uses rest of descriptors leaved by
|
|
|
|
+ * weighted channels.
|
|
|
|
+ */
|
|
|
|
+int cpdma_chan_set_weight(struct cpdma_chan *ch, int weight)
|
|
|
|
+{
|
|
|
|
+ struct cpdma_ctlr *ctlr = ch->ctlr;
|
|
|
|
+ unsigned long flags, ch_flags;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&ctlr->lock, flags);
|
|
|
|
+ spin_lock_irqsave(&ch->lock, ch_flags);
|
|
|
|
+ if (ch->weight == weight) {
|
|
|
|
+ spin_unlock_irqrestore(&ch->lock, ch_flags);
|
|
|
|
+ spin_unlock_irqrestore(&ctlr->lock, flags);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ ch->weight = weight;
|
|
|
|
+ spin_unlock_irqrestore(&ch->lock, ch_flags);
|
|
|
|
+
|
|
|
|
+ /* re-split pool using new channel weight */
|
|
|
|
+ ret = cpdma_chan_split_pool(ctlr);
|
|
|
|
+ spin_unlock_irqrestore(&ctlr->lock, flags);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
|
|
struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
|
|
@@ -527,6 +630,7 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
|
|
chan->chan_num = chan_num;
|
|
chan->chan_num = chan_num;
|
|
chan->handler = handler;
|
|
chan->handler = handler;
|
|
chan->desc_num = ctlr->pool->num_desc / 2;
|
|
chan->desc_num = ctlr->pool->num_desc / 2;
|
|
|
|
+ chan->weight = 0;
|
|
|
|
|
|
if (is_rx_chan(chan)) {
|
|
if (is_rx_chan(chan)) {
|
|
chan->hdp = ctlr->params.rxhdp + offset;
|
|
chan->hdp = ctlr->params.rxhdp + offset;
|