Commit cfd96d3e authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'keystone-driver-soc_v2' of...

Merge tag 'keystone-driver-soc_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone into next/soc

Merge "ARM Keystone SOC driver updates for 4.4" from Santosh Shilimkar:

	Documentation and support to be able to load the PDSP
	firmware necessary for accumulator operation.

* tag 'keystone-driver-soc_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone:
  soc: ti: qmss: make acc queue support optional in the driver
  soc: ti: add firmware file name as part of the driver
  Documentation: dt: soc: Add description for knav qmss driver
parents d72b7128 04501690
* Texas Instruments Keystone Navigator Queue Management SubSystem driver
Driver source code path
drivers/soc/ti/knav_qmss.c
drivers/soc/ti/knav_qmss_acc.c
The QMSS (Queue Manager Sub System) found on Keystone SOCs is one of
the main hardware sub system which forms the backbone of the Keystone
multi-core Navigator. QMSS consist of queue managers, packed-data structure
processors(PDSP), linking RAM, descriptor pools and infrastructure
Packet DMA.
The Queue Manager is a hardware module that is responsible for accelerating
management of the packet queues. Packets are queued/de-queued by writing or
reading descriptor address to a particular memory mapped location. The PDSPs
perform QMSS related functions like accumulation, QoS, or event management.
Linking RAM registers are used to link the descriptors which are stored in
descriptor RAM. Descriptor RAM is configurable as internal or external memory.
The QMSS driver manages the PDSP setups, linking RAM regions,
queue pool management (allocation, push, pop and notify) and descriptor
pool management.
knav qmss driver provides a set of APIs to drivers to open/close qmss queues,
allocate descriptor pools, map the descriptors, push/pop to queues etc. For
details of the available APIs, please refers to include/linux/soc/ti/knav_qmss.h
DT documentation is available at
Documentation/devicetree/bindings/soc/ti/keystone-navigator-qmss.txt
Accumulator QMSS queues using PDSP firmware
============================================
The QMSS PDSP firmware support accumulator channel that can monitor a single
queue or multiple contiguous queues. drivers/soc/ti/knav_qmss_acc.c is the
driver that interface with the accumulator PDSP. This configures
accumulator channels defined in DTS (example in DT documentation) to monitor
1 or 32 queues per channel. More description on the firmware is available in
CPPI/QMSS Low Level Driver document (docs/CPPI_QMSS_LLD_SDS.pdf) at
git://git.ti.com/keystone-rtos/qmss-lld.git
k2_qmss_pdsp_acc48_k2_le_1_0_0_9.bin firmware supports upto 48 accumulator
channels. This firmware is available under ti-keystone folder of
firmware.git at
git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
To use copy the firmware image to lib/firmware folder of the initramfs or
ubifs file system and provide a sym link to k2_qmss_pdsp_acc48_k2_le_1_0_0_9.bin
in the file system and boot up the kernel. User would see
"firmware file ks2_qmss_pdsp_acc48.bin downloaded for PDSP"
in the boot up log if loading of firmware to PDSP is successful.
Use of accumulated queues requires the firmware image to be present in the
file system. The driver doesn't acc queues to the supported queue range if
PDSP is not running in the SoC. The API call fails if there is a queue open
request to an acc queue and PDSP is not running. So make sure to copy firmware
to file system before using these queue types.
...@@ -221,7 +221,6 @@ qmss: qmss@2a40000 { ...@@ -221,7 +221,6 @@ qmss: qmss@2a40000 {
#size-cells = <1>; #size-cells = <1>;
ranges; ranges;
pdsp0@0x2a10000 { pdsp0@0x2a10000 {
firmware = "keystone/qmss_pdsp_acc48_k2_le_1_0_0_8.fw";
reg = <0x2a10000 0x1000>, reg = <0x2a10000 0x1000>,
<0x2a0f000 0x100>, <0x2a0f000 0x100>,
<0x2a0c000 0x3c8>, <0x2a0c000 0x3c8>,
......
...@@ -135,9 +135,10 @@ struct knav_pdsp_info { ...@@ -135,9 +135,10 @@ struct knav_pdsp_info {
}; };
void __iomem *intd; void __iomem *intd;
u32 __iomem *iram; u32 __iomem *iram;
const char *firmware;
u32 id; u32 id;
struct list_head list; struct list_head list;
bool loaded;
bool started;
}; };
struct knav_qmgr_info { struct knav_qmgr_info {
......
...@@ -482,8 +482,8 @@ struct knav_range_ops knav_acc_range_ops = { ...@@ -482,8 +482,8 @@ struct knav_range_ops knav_acc_range_ops = {
* Return 0 on success or error * Return 0 on success or error
*/ */
int knav_init_acc_range(struct knav_device *kdev, int knav_init_acc_range(struct knav_device *kdev,
struct device_node *node, struct device_node *node,
struct knav_range_info *range) struct knav_range_info *range)
{ {
struct knav_acc_channel *acc; struct knav_acc_channel *acc;
struct knav_pdsp_info *pdsp; struct knav_pdsp_info *pdsp;
...@@ -526,6 +526,12 @@ int knav_init_acc_range(struct knav_device *kdev, ...@@ -526,6 +526,12 @@ int knav_init_acc_range(struct knav_device *kdev,
return -EINVAL; return -EINVAL;
} }
if (!pdsp->started) {
dev_err(kdev->dev, "pdsp id %d not started for range %s\n",
info->pdsp_id, range->name);
return -ENODEV;
}
info->pdsp = pdsp; info->pdsp = pdsp;
channels = range->num_queues; channels = range->num_queues;
if (of_get_property(node, "multi-queue", NULL)) { if (of_get_property(node, "multi-queue", NULL)) {
......
...@@ -68,6 +68,12 @@ static DEFINE_MUTEX(knav_dev_lock); ...@@ -68,6 +68,12 @@ static DEFINE_MUTEX(knav_dev_lock);
idx < (kdev)->num_queues_in_use; \ idx < (kdev)->num_queues_in_use; \
idx++, inst = knav_queue_idx_to_inst(kdev, idx)) idx++, inst = knav_queue_idx_to_inst(kdev, idx))
/* All firmware file names end up here. List the firmware file names below.
* Newest followed by older ones. Search is done from start of the array
* until a firmware file is found.
*/
const char *knav_acc_firmwares[] = {"ks2_qmss_pdsp_acc48.bin"};
/** /**
* knav_queue_notify: qmss queue notfier call * knav_queue_notify: qmss queue notfier call
* *
...@@ -1439,7 +1445,6 @@ static int knav_queue_init_pdsps(struct knav_device *kdev, ...@@ -1439,7 +1445,6 @@ static int knav_queue_init_pdsps(struct knav_device *kdev,
struct device *dev = kdev->dev; struct device *dev = kdev->dev;
struct knav_pdsp_info *pdsp; struct knav_pdsp_info *pdsp;
struct device_node *child; struct device_node *child;
int ret;
for_each_child_of_node(pdsps, child) { for_each_child_of_node(pdsps, child) {
pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL); pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL);
...@@ -1448,17 +1453,6 @@ static int knav_queue_init_pdsps(struct knav_device *kdev, ...@@ -1448,17 +1453,6 @@ static int knav_queue_init_pdsps(struct knav_device *kdev,
return -ENOMEM; return -ENOMEM;
} }
pdsp->name = knav_queue_find_name(child); pdsp->name = knav_queue_find_name(child);
ret = of_property_read_string(child, "firmware",
&pdsp->firmware);
if (ret < 0 || !pdsp->firmware) {
dev_err(dev, "unknown firmware for pdsp %s\n",
pdsp->name);
devm_kfree(dev, pdsp);
continue;
}
dev_dbg(dev, "pdsp name %s fw name :%s\n", pdsp->name,
pdsp->firmware);
pdsp->iram = pdsp->iram =
knav_queue_map_reg(kdev, child, knav_queue_map_reg(kdev, child,
KNAV_QUEUE_PDSP_IRAM_REG_INDEX); KNAV_QUEUE_PDSP_IRAM_REG_INDEX);
...@@ -1489,9 +1483,9 @@ static int knav_queue_init_pdsps(struct knav_device *kdev, ...@@ -1489,9 +1483,9 @@ static int knav_queue_init_pdsps(struct knav_device *kdev,
} }
of_property_read_u32(child, "id", &pdsp->id); of_property_read_u32(child, "id", &pdsp->id);
list_add_tail(&pdsp->list, &kdev->pdsps); list_add_tail(&pdsp->list, &kdev->pdsps);
dev_dbg(dev, "added pdsp %s: command %p, iram %p, regs %p, intd %p, firmware %s\n", dev_dbg(dev, "added pdsp %s: command %p, iram %p, regs %p, intd %p\n",
pdsp->name, pdsp->command, pdsp->iram, pdsp->regs, pdsp->name, pdsp->command, pdsp->iram, pdsp->regs,
pdsp->intd, pdsp->firmware); pdsp->intd);
} }
return 0; return 0;
} }
...@@ -1510,6 +1504,8 @@ static int knav_queue_stop_pdsp(struct knav_device *kdev, ...@@ -1510,6 +1504,8 @@ static int knav_queue_stop_pdsp(struct knav_device *kdev,
dev_err(kdev->dev, "timed out on pdsp %s stop\n", pdsp->name); dev_err(kdev->dev, "timed out on pdsp %s stop\n", pdsp->name);
return ret; return ret;
} }
pdsp->loaded = false;
pdsp->started = false;
return 0; return 0;
} }
...@@ -1518,14 +1514,29 @@ static int knav_queue_load_pdsp(struct knav_device *kdev, ...@@ -1518,14 +1514,29 @@ static int knav_queue_load_pdsp(struct knav_device *kdev,
{ {
int i, ret, fwlen; int i, ret, fwlen;
const struct firmware *fw; const struct firmware *fw;
bool found = false;
u32 *fwdata; u32 *fwdata;
ret = request_firmware(&fw, pdsp->firmware, kdev->dev); for (i = 0; i < ARRAY_SIZE(knav_acc_firmwares); i++) {
if (ret) { if (knav_acc_firmwares[i]) {
dev_err(kdev->dev, "failed to get firmware %s for pdsp %s\n", ret = request_firmware(&fw,
pdsp->firmware, pdsp->name); knav_acc_firmwares[i],
return ret; kdev->dev);
if (!ret) {
found = true;
break;
}
}
}
if (!found) {
dev_err(kdev->dev, "failed to get firmware for pdsp\n");
return -ENODEV;
} }
dev_info(kdev->dev, "firmware file %s downloaded for PDSP\n",
knav_acc_firmwares[i]);
writel_relaxed(pdsp->id + 1, pdsp->command + 0x18); writel_relaxed(pdsp->id + 1, pdsp->command + 0x18);
/* download the firmware */ /* download the firmware */
fwdata = (u32 *)fw->data; fwdata = (u32 *)fw->data;
...@@ -1583,16 +1594,24 @@ static int knav_queue_start_pdsps(struct knav_device *kdev) ...@@ -1583,16 +1594,24 @@ static int knav_queue_start_pdsps(struct knav_device *kdev)
int ret; int ret;
knav_queue_stop_pdsps(kdev); knav_queue_stop_pdsps(kdev);
/* now load them all */ /* now load them all. We return success even if pdsp
* is not loaded as acc channels are optional on having
* firmware availability in the system. We set the loaded
* and stated flag and when initialize the acc range, check
* it and init the range only if pdsp is started.
*/
for_each_pdsp(kdev, pdsp) { for_each_pdsp(kdev, pdsp) {
ret = knav_queue_load_pdsp(kdev, pdsp); ret = knav_queue_load_pdsp(kdev, pdsp);
if (ret < 0) if (!ret)
return ret; pdsp->loaded = true;
} }
for_each_pdsp(kdev, pdsp) { for_each_pdsp(kdev, pdsp) {
ret = knav_queue_start_pdsp(kdev, pdsp); if (pdsp->loaded) {
WARN_ON(ret); ret = knav_queue_start_pdsp(kdev, pdsp);
if (!ret)
pdsp->started = true;
}
} }
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment