[PATCH] drivers/tty: amba-pl011: defer driver probing if external dma is not ready.
Jorge Ramirez-Ortiz
jorge.ramirez-ortiz at linaro.org
Tue Feb 24 14:02:08 PST 2015
On 02/24/2015 02:32 PM, Jorge Ramirez-Ortiz wrote:
> On 02/24/2015 03:29 AM, Arnd Bergmann wrote:
>> On Monday 23 February 2015 22:46:53 Jorge Ramirez-Ortiz wrote:
>>> This patch addresses a race condition that happens when
>>> device_initcall(pl011_dma_initicall) is executed before all the devices have been
>>> probed - this issue was observed on an hisi_6220 SoC (HiKey board from Linaro).
>> How do you want to handle the case where there is a DMA channel in DT, but
>> no driver for it built into the kernel?
>
> In my view that should be fixed in the of-dma and the acpi-dma implementation
> (just don't return EPROBE_DEFER a second time since the driver wasn't found
> after the late_initcall associated to deferred probing). That would allow
> drivers that use the defer probing mechanism to wait for optional controllers to
> complete their probing sequence if those controllers don't become available.
>
> In any case, I can track the pending requests (up to twelve, one per UART) with
> a list in the driver.
> If a second request to probe fails with EPROBE_DEFER we can conclude that the
> driver is not present.
>
> I have tested such an implementation and it works as expected.
> would that be acceptable?
Something along these lines
+struct dma_deferred {
+ struct list_head node;
+ struct device *dev;
+};
+
+static LIST_HEAD(dma_deferred_reqs);
+
+static int pl011_handle_dma_probe_defer(struct device *dev)
+{
+ struct list_head *node, *tmp;
+ struct dma_deferred *entry;
+
+ list_for_each_safe(node, tmp, &dma_deferred_reqs) {
+ entry = list_entry(node, struct dma_deferred, node);
+ if (entry->dev == dev) {
+ dev_warn(dev, "DMA driver not present \n");
+ list_del(node);
+ kfree(entry);
+ return -ENODEV;
+ }
+ }
+
+ entry = kzalloc(sizeof(struct dma_deferred), GFP_KERNEL);
+ if (entry) {
+ entry->dev = dev;
+ list_add_tail(&entry->node, &dma_deferred_reqs);
+ dev_info(dev, "DMA controller not ready \n");
+ return -EPROBE_DEFER;
+ }
+
+ return 0;
+}
+
+static int pl011_dma_probe(struct device *dev, struct uart_amba_port *uap,
+ void (*queue_dma_probe)(struct device *, struct uart_amba_port *))
{
/* DMA is the sole user of the platform data right now */
struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev);
@@ -275,15 +310,25 @@ static void pl011_dma_probe_initcall(struct device *dev,
struct uart_amba_port *
struct dma_chan *chan;
dma_cap_mask_t mask;
- chan = dma_request_slave_channel(dev, "tx");
+ if (queue_dma_probe && plat && plat->dma_filter) {
+ (*queue_dma_probe)(dev, uap);
+ return 0;
+ }
+
+ chan = dma_request_slave_channel_reason(dev, "tx");
+ if (IS_ERR(chan)) {
+
+ /* the dma driver has not been initialized yet */
+ if (PTR_ERR(chan) == -EPROBE_DEFER)
+ return pl011_handle_dma_probe_defer(dev);
+
+ dev_info(uap->port.dev, "no OF or ACPI data \n");
>
>>> @@ -275,15 +277,23 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
>>> struct dma_chan *chan;
>>> dma_cap_mask_t mask;
>>>
>>> - chan = dma_request_slave_channel(dev, "tx");
>>> + if (queue_dma_probe && plat && plat->dma_filter) {
>>> + (*queue_dma_probe)(dev, uap);
>>> + return 0;
>>> + }
>>> +
>>> + chan = dma_request_slave_channel_reason(dev, "tx");
>>> + if (IS_ERR(chan)) {
>>> +
>>> + /* the dma driver has not been initialized yet */
>>> + if (PTR_ERR(chan) == -EPROBE_DEFER)
>>> + return -EPROBE_DEFER;
>>>
>>> - if (!chan) {
>>> /* We need platform data */
>>> if (!plat || !plat->dma_filter) {
>>> dev_info(uap->port.dev, "no DMA platform data\n");
>>> - return;
>>> + return 0;
>>> }
>> It would be nice to print the error code here now that you know it
>> and can tell the difference between no DMA channel being supplied
>> and the DMA channel from the DT properties being unavailable.
> ACKd
>>> /* Try to acquire a generic DMA engine slave TX channel */
>>> dma_cap_zero(mask);
>>> dma_cap_set(DMA_SLAVE, mask);
>>> @@ -292,7 +302,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
>>> plat->dma_tx_param);
>>> if (!chan) {
>>> dev_err(uap->port.dev, "no TX DMA channel!\n");
>>> - return;
>>> + return 0;
>>> }
>>> }
>>>
>>> @@ -310,7 +320,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
>>>
>>> if (!chan) {
>>> dev_err(uap->port.dev, "no RX DMA channel!\n");
>>> - return;
>>> + return 0;
>>> }
>>> }
>> I think the condition is wrong now that 'chan' contains a ERR_PTR
>> value in case of failure.
> Actually that is not the case on the RX DMA case: on the RX DMA case, chan
> contains either NULL or a valid pointer (not an ERR_PTR value)
>
> chan only contains an ERR_PTR on the first attempt to access the DMA controller
> (just so we can handle the defer probing case)
> On subsequent accesses, it assumes that it is there (since it would have been
> deferred if it wasn't).
>
>
>
More information about the linux-arm-kernel
mailing list