aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King - ARM Linux <linux@arm.linux.org.uk>2011-01-03 17:41:13 -0500
committerDan Williams <dan.j.williams@intel.com>2011-01-04 22:16:13 -0500
commit30749cb4a40f02a199640011e5ab5c5f60b8482e (patch)
tree2b079f8f3fb2f888d7afa36423b41c63a1583c3b
parentc7da9a56d608145cc763bcfc9329b92c4244d8d9 (diff)
ARM: PL08x: allow AHB master port selection to be configured
Platforms need to be able to control which AHB master interface is used, as each AHB master interface may be asymetric. Allow the interfaces used for fetching LLIs, memory, and each peripheral to be configured individually. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/dma/amba-pl08x.c77
-rw-r--r--include/linux/amba/pl08x.h15
2 files changed, 56 insertions, 36 deletions
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index 4ee0ab19b1b6..986c12775b0b 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -126,6 +126,8 @@ struct pl08x_lli {
126 * @phy_chans: array of data for the physical channels 126 * @phy_chans: array of data for the physical channels
127 * @pool: a pool for the LLI descriptors 127 * @pool: a pool for the LLI descriptors
128 * @pool_ctr: counter of LLIs in the pool 128 * @pool_ctr: counter of LLIs in the pool
129 * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI fetches
130 * @mem_buses: set to indicate memory transfers on AHB2.
129 * @lock: a spinlock for this struct 131 * @lock: a spinlock for this struct
130 */ 132 */
131struct pl08x_driver_data { 133struct pl08x_driver_data {
@@ -138,6 +140,8 @@ struct pl08x_driver_data {
138 struct pl08x_phy_chan *phy_chans; 140 struct pl08x_phy_chan *phy_chans;
139 struct dma_pool *pool; 141 struct dma_pool *pool;
140 int pool_ctr; 142 int pool_ctr;
143 u8 lli_buses;
144 u8 mem_buses;
141 spinlock_t lock; 145 spinlock_t lock;
142}; 146};
143 147
@@ -526,20 +530,12 @@ static int pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
526 530
527 BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS); 531 BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS);
528 532
529 llis_va[num_llis].cctl = cctl; 533 llis_va[num_llis].cctl = cctl;
530 llis_va[num_llis].src = txd->srcbus.addr; 534 llis_va[num_llis].src = txd->srcbus.addr;
531 llis_va[num_llis].dst = txd->dstbus.addr; 535 llis_va[num_llis].dst = txd->dstbus.addr;
532
533 /*
534 * On versions with dual masters, you can optionally AND on
535 * PL080_LLI_LM_AHB2 to the LLI to tell the hardware to read
536 * in new LLIs with that controller, but we always try to
537 * choose AHB1 to point into memory. The idea is to have AHB2
538 * fixed on the peripheral and AHB1 messing around in the
539 * memory. So we don't manipulate this bit currently.
540 */
541
542 llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli); 536 llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
537 if (pl08x->lli_buses & PL08X_AHB2)
538 llis_va[num_llis].lli |= PL080_LLI_LM_AHB2;
543 539
544 if (cctl & PL080_CONTROL_SRC_INCR) 540 if (cctl & PL080_CONTROL_SRC_INCR)
545 txd->srcbus.addr += len; 541 txd->srcbus.addr += len;
@@ -639,13 +635,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
639 pl08x_choose_master_bus(&txd->srcbus, &txd->dstbus, 635 pl08x_choose_master_bus(&txd->srcbus, &txd->dstbus,
640 &mbus, &sbus, cctl); 636 &mbus, &sbus, cctl);
641 637
642
643 /*
644 * The lowest bit of the LLI register
645 * is also used to indicate which master to
646 * use for reading the LLIs.
647 */
648
649 if (txd->len < mbus->buswidth) { 638 if (txd->len < mbus->buswidth) {
650 /* 639 /*
651 * Less than a bus width available 640 * Less than a bus width available
@@ -1282,6 +1271,23 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
1282 return 0; 1271 return 0;
1283} 1272}
1284 1273
1274/*
1275 * Given the source and destination available bus masks, select which
1276 * will be routed to each port. We try to have source and destination
1277 * on separate ports, but always respect the allowable settings.
1278 */
1279static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst)
1280{
1281 u32 cctl = 0;
1282
1283 if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
1284 cctl |= PL080_CONTROL_DST_AHB2;
1285 if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
1286 cctl |= PL080_CONTROL_SRC_AHB2;
1287
1288 return cctl;
1289}
1290
1285static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan) 1291static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan)
1286{ 1292{
1287 struct pl08x_txd *txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT); 1293 struct pl08x_txd *txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
@@ -1330,15 +1336,9 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
1330 /* Both to be incremented or the code will break */ 1336 /* Both to be incremented or the code will break */
1331 txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR; 1337 txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
1332 1338
1333 /*
1334 * On the PL080 we have two bus masters and we should select one for
1335 * source and one for destination. We try to use AHB2 for the bus
1336 * which does not increment (typically the peripheral) else we just
1337 * choose something.
1338 */
1339 if (pl08x->vd->dualmaster) 1339 if (pl08x->vd->dualmaster)
1340 /* Source increments, use AHB2 for destination */ 1340 txd->cctl |= pl08x_select_bus(pl08x,
1341 txd->cctl |= PL080_CONTROL_DST_AHB2; 1341 pl08x->mem_buses, pl08x->mem_buses);
1342 1342
1343 ret = pl08x_prep_channel_resources(plchan, txd); 1343 ret = pl08x_prep_channel_resources(plchan, txd);
1344 if (ret) 1344 if (ret)
@@ -1359,6 +1359,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
1359 struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); 1359 struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
1360 struct pl08x_driver_data *pl08x = plchan->host; 1360 struct pl08x_driver_data *pl08x = plchan->host;
1361 struct pl08x_txd *txd; 1361 struct pl08x_txd *txd;
1362 u8 src_buses, dst_buses;
1362 int ret; 1363 int ret;
1363 1364
1364 /* 1365 /*
@@ -1403,31 +1404,31 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
1403 if (direction == DMA_TO_DEVICE) { 1404 if (direction == DMA_TO_DEVICE) {
1404 txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT; 1405 txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
1405 txd->cctl |= PL080_CONTROL_SRC_INCR; 1406 txd->cctl |= PL080_CONTROL_SRC_INCR;
1406 if (pl08x->vd->dualmaster)
1407 /* Source increments, use AHB2 for destination */
1408 txd->cctl |= PL080_CONTROL_DST_AHB2;
1409 txd->srcbus.addr = sgl->dma_address; 1407 txd->srcbus.addr = sgl->dma_address;
1410 if (plchan->runtime_addr) 1408 if (plchan->runtime_addr)
1411 txd->dstbus.addr = plchan->runtime_addr; 1409 txd->dstbus.addr = plchan->runtime_addr;
1412 else 1410 else
1413 txd->dstbus.addr = plchan->cd->addr; 1411 txd->dstbus.addr = plchan->cd->addr;
1412 src_buses = pl08x->mem_buses;
1413 dst_buses = plchan->cd->periph_buses;
1414 } else if (direction == DMA_FROM_DEVICE) { 1414 } else if (direction == DMA_FROM_DEVICE) {
1415 txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; 1415 txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
1416 txd->cctl |= PL080_CONTROL_DST_INCR; 1416 txd->cctl |= PL080_CONTROL_DST_INCR;
1417 if (pl08x->vd->dualmaster)
1418 /* Destination increments, use AHB2 for source */
1419 txd->cctl |= PL080_CONTROL_SRC_AHB2;
1420 if (plchan->runtime_addr) 1417 if (plchan->runtime_addr)
1421 txd->srcbus.addr = plchan->runtime_addr; 1418 txd->srcbus.addr = plchan->runtime_addr;
1422 else 1419 else
1423 txd->srcbus.addr = plchan->cd->addr; 1420 txd->srcbus.addr = plchan->cd->addr;
1424 txd->dstbus.addr = sgl->dma_address; 1421 txd->dstbus.addr = sgl->dma_address;
1422 src_buses = plchan->cd->periph_buses;
1423 dst_buses = pl08x->mem_buses;
1425 } else { 1424 } else {
1426 dev_err(&pl08x->adev->dev, 1425 dev_err(&pl08x->adev->dev,
1427 "%s direction unsupported\n", __func__); 1426 "%s direction unsupported\n", __func__);
1428 return NULL; 1427 return NULL;
1429 } 1428 }
1430 1429
1430 txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
1431
1431 ret = pl08x_prep_channel_resources(plchan, txd); 1432 ret = pl08x_prep_channel_resources(plchan, txd);
1432 if (ret) 1433 if (ret)
1433 return NULL; 1434 return NULL;
@@ -1879,6 +1880,14 @@ static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
1879 pl08x->adev = adev; 1880 pl08x->adev = adev;
1880 pl08x->vd = vd; 1881 pl08x->vd = vd;
1881 1882
1883 /* By default, AHB1 only. If dualmaster, from platform */
1884 pl08x->lli_buses = PL08X_AHB1;
1885 pl08x->mem_buses = PL08X_AHB1;
1886 if (pl08x->vd->dualmaster) {
1887 pl08x->lli_buses = pl08x->pd->lli_buses;
1888 pl08x->mem_buses = pl08x->pd->mem_buses;
1889 }
1890
1882 /* A DMA memory pool for LLIs, align on 1-byte boundary */ 1891 /* A DMA memory pool for LLIs, align on 1-byte boundary */
1883 pl08x->pool = dma_pool_create(DRIVER_NAME, &pl08x->adev->dev, 1892 pl08x->pool = dma_pool_create(DRIVER_NAME, &pl08x->adev->dev,
1884 PL08X_LLI_TSFR_SIZE, PL08X_ALIGN, 0); 1893 PL08X_LLI_TSFR_SIZE, PL08X_ALIGN, 0);
diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h
index 8d9083067d3d..f858651027fd 100644
--- a/include/linux/amba/pl08x.h
+++ b/include/linux/amba/pl08x.h
@@ -25,6 +25,12 @@
25struct pl08x_lli; 25struct pl08x_lli;
26struct pl08x_driver_data; 26struct pl08x_driver_data;
27 27
28/* Bitmasks for selecting AHB ports for DMA transfers */
29enum {
30 PL08X_AHB1 = (1 << 0),
31 PL08X_AHB2 = (1 << 1)
32};
33
28/** 34/**
29 * struct pl08x_channel_data - data structure to pass info between 35 * struct pl08x_channel_data - data structure to pass info between
30 * platform and PL08x driver regarding channel configuration 36 * platform and PL08x driver regarding channel configuration
@@ -51,6 +57,8 @@ struct pl08x_driver_data;
51 * round round round) 57 * round round round)
52 * @single: the device connected to this channel will request single 58 * @single: the device connected to this channel will request single
53 * DMA transfers, not bursts. (Bursts are default.) 59 * DMA transfers, not bursts. (Bursts are default.)
60 * @periph_buses: the device connected to this channel is accessible via
61 * these buses (use PL08X_AHB1 | PL08X_AHB2).
54 */ 62 */
55struct pl08x_channel_data { 63struct pl08x_channel_data {
56 char *bus_id; 64 char *bus_id;
@@ -61,6 +69,7 @@ struct pl08x_channel_data {
61 dma_addr_t addr; 69 dma_addr_t addr;
62 bool circular_buffer; 70 bool circular_buffer;
63 bool single; 71 bool single;
72 u8 periph_buses;
64}; 73};
65 74
66/** 75/**
@@ -193,8 +202,8 @@ struct pl08x_dma_chan {
193 * less than zero, else it returns the allocated signal number 202 * less than zero, else it returns the allocated signal number
194 * @put_signal: indicate to the platform that this physical signal is not 203 * @put_signal: indicate to the platform that this physical signal is not
195 * running any DMA transfer and multiplexing can be recycled 204 * running any DMA transfer and multiplexing can be recycled
196 * @bus_bit_lli: Bit[0] of the address indicated which AHB bus master the 205 * @lli_buses: buses which LLIs can be fetched from: PL08X_AHB1 | PL08X_AHB2
197 * LLI addresses are on 0/1 Master 1/2. 206 * @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2
198 */ 207 */
199struct pl08x_platform_data { 208struct pl08x_platform_data {
200 struct pl08x_channel_data *slave_channels; 209 struct pl08x_channel_data *slave_channels;
@@ -202,6 +211,8 @@ struct pl08x_platform_data {
202 struct pl08x_channel_data memcpy_channel; 211 struct pl08x_channel_data memcpy_channel;
203 int (*get_signal)(struct pl08x_dma_chan *); 212 int (*get_signal)(struct pl08x_dma_chan *);
204 void (*put_signal)(struct pl08x_dma_chan *); 213 void (*put_signal)(struct pl08x_dma_chan *);
214 u8 lli_buses;
215 u8 mem_buses;
205}; 216};
206 217
207#ifdef CONFIG_AMBA_PL08X 218#ifdef CONFIG_AMBA_PL08X