Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
90644ad7
Commit
90644ad7
authored
Dec 14, 2016
by
Vinod Koul
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/qcom' into for-linus
parents
83cb0dca
75ff7668
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
280 additions
and
106 deletions
+280
-106
Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt
Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt
+9
-3
drivers/dma/qcom/hidma.c
drivers/dma/qcom/hidma.c
+161
-12
drivers/dma/qcom/hidma.h
drivers/dma/qcom/hidma.h
+8
-1
drivers/dma/qcom/hidma_dbg.c
drivers/dma/qcom/hidma_dbg.c
+2
-2
drivers/dma/qcom/hidma_ll.c
drivers/dma/qcom/hidma_ll.c
+90
-86
drivers/dma/qcom/hidma_mgmt.c
drivers/dma/qcom/hidma_mgmt.c
+9
-2
drivers/of/irq.c
drivers/of/irq.c
+1
-0
No files found.
Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt
View file @
90644ad7
...
...
@@ -5,13 +5,13 @@ memcpy and memset capabilities. It has been designed for virtualized
environments.
Each HIDMA HW instance consists of multiple DMA channels. These channels
share the same bandwidth. The bandwidth utilization can be par
i
titioned
share the same bandwidth. The bandwidth utilization can be partitioned
among channels based on the priority and weight assignments.
There are only two priority levels and 15 weigh assignments possible.
Other parameters here determine how much of the system bus this HIDMA
instance can use like maximum read/write request and
and
number of bytes to
instance can use like maximum read/write request and number of bytes to
read/write in a single burst.
Main node required properties:
...
...
@@ -47,12 +47,18 @@ When the OS is not in control of the management interface (i.e. it's a guest),
the channel nodes appear on their own, not under a management node.
Required properties:
- compatible: must contain "qcom,hidma-1.0"
- compatible: must contain "qcom,hidma-1.0" for initial HW or "qcom,hidma-1.1"
for MSI capable HW.
- reg: Addresses for the transfer and event channel
- interrupts: Should contain the event interrupt
- desc-count: Number of asynchronous requests this channel can handle
- iommus: required a iommu node
Optional properties for MSI:
- msi-parent : See the generic MSI binding described in
devicetree/bindings/interrupt-controller/msi.txt for a description of the
msi-parent property.
Example:
Hypervisor OS configuration:
...
...
drivers/dma/qcom/hidma.c
View file @
90644ad7
...
...
@@ -56,6 +56,7 @@
#include <linux/irq.h>
#include <linux/atomic.h>
#include <linux/pm_runtime.h>
#include <linux/msi.h>
#include "../dmaengine.h"
#include "hidma.h"
...
...
@@ -70,6 +71,7 @@
#define HIDMA_ERR_INFO_SW 0xFF
#define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE 0x0
#define HIDMA_NR_DEFAULT_DESC 10
#define HIDMA_MSI_INTS 11
static
inline
struct
hidma_dev
*
to_hidma_dev
(
struct
dma_device
*
dmadev
)
{
...
...
@@ -553,6 +555,17 @@ static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
return
hidma_ll_inthandler
(
chirq
,
lldev
);
}
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
static
irqreturn_t
hidma_chirq_handler_msi
(
int
chirq
,
void
*
arg
)
{
struct
hidma_lldev
**
lldevp
=
arg
;
struct
hidma_dev
*
dmadev
=
to_hidma_dev_from_lldev
(
lldevp
);
return
hidma_ll_inthandler_msi
(
chirq
,
*
lldevp
,
1
<<
(
chirq
-
dmadev
->
msi_virqbase
));
}
#endif
static
ssize_t
hidma_show_values
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
...
...
@@ -567,8 +580,13 @@ static ssize_t hidma_show_values(struct device *dev,
return
strlen
(
buf
);
}
static
int
hidma_create_sysfs_entry
(
struct
hidma_dev
*
dev
,
char
*
name
,
int
mode
)
static
inline
void
hidma_sysfs_uninit
(
struct
hidma_dev
*
dev
)
{
device_remove_file
(
dev
->
ddev
.
dev
,
dev
->
chid_attrs
);
}
static
struct
device_attribute
*
hidma_create_sysfs_entry
(
struct
hidma_dev
*
dev
,
char
*
name
,
int
mode
)
{
struct
device_attribute
*
attrs
;
char
*
name_copy
;
...
...
@@ -576,18 +594,125 @@ static int hidma_create_sysfs_entry(struct hidma_dev *dev, char *name,
attrs
=
devm_kmalloc
(
dev
->
ddev
.
dev
,
sizeof
(
struct
device_attribute
),
GFP_KERNEL
);
if
(
!
attrs
)
return
-
ENOMEM
;
return
NULL
;
name_copy
=
devm_kstrdup
(
dev
->
ddev
.
dev
,
name
,
GFP_KERNEL
);
if
(
!
name_copy
)
return
-
ENOMEM
;
return
NULL
;
attrs
->
attr
.
name
=
name_copy
;
attrs
->
attr
.
mode
=
mode
;
attrs
->
show
=
hidma_show_values
;
sysfs_attr_init
(
&
attrs
->
attr
);
return
device_create_file
(
dev
->
ddev
.
dev
,
attrs
);
return
attrs
;
}
static
int
hidma_sysfs_init
(
struct
hidma_dev
*
dev
)
{
dev
->
chid_attrs
=
hidma_create_sysfs_entry
(
dev
,
"chid"
,
S_IRUGO
);
if
(
!
dev
->
chid_attrs
)
return
-
ENOMEM
;
return
device_create_file
(
dev
->
ddev
.
dev
,
dev
->
chid_attrs
);
}
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
static
void
hidma_write_msi_msg
(
struct
msi_desc
*
desc
,
struct
msi_msg
*
msg
)
{
struct
device
*
dev
=
msi_desc_to_dev
(
desc
);
struct
hidma_dev
*
dmadev
=
dev_get_drvdata
(
dev
);
if
(
!
desc
->
platform
.
msi_index
)
{
writel
(
msg
->
address_lo
,
dmadev
->
dev_evca
+
0x118
);
writel
(
msg
->
address_hi
,
dmadev
->
dev_evca
+
0x11C
);
writel
(
msg
->
data
,
dmadev
->
dev_evca
+
0x120
);
}
}
#endif
static
void
hidma_free_msis
(
struct
hidma_dev
*
dmadev
)
{
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct
device
*
dev
=
dmadev
->
ddev
.
dev
;
struct
msi_desc
*
desc
;
/* free allocated MSI interrupts above */
for_each_msi_entry
(
desc
,
dev
)
devm_free_irq
(
dev
,
desc
->
irq
,
&
dmadev
->
lldev
);
platform_msi_domain_free_irqs
(
dev
);
#endif
}
static
int
hidma_request_msi
(
struct
hidma_dev
*
dmadev
,
struct
platform_device
*
pdev
)
{
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
int
rc
;
struct
msi_desc
*
desc
;
struct
msi_desc
*
failed_desc
=
NULL
;
rc
=
platform_msi_domain_alloc_irqs
(
&
pdev
->
dev
,
HIDMA_MSI_INTS
,
hidma_write_msi_msg
);
if
(
rc
)
return
rc
;
for_each_msi_entry
(
desc
,
&
pdev
->
dev
)
{
if
(
!
desc
->
platform
.
msi_index
)
dmadev
->
msi_virqbase
=
desc
->
irq
;
rc
=
devm_request_irq
(
&
pdev
->
dev
,
desc
->
irq
,
hidma_chirq_handler_msi
,
0
,
"qcom-hidma-msi"
,
&
dmadev
->
lldev
);
if
(
rc
)
{
failed_desc
=
desc
;
break
;
}
}
if
(
rc
)
{
/* free allocated MSI interrupts above */
for_each_msi_entry
(
desc
,
&
pdev
->
dev
)
{
if
(
desc
==
failed_desc
)
break
;
devm_free_irq
(
&
pdev
->
dev
,
desc
->
irq
,
&
dmadev
->
lldev
);
}
}
else
{
/* Add callback to free MSIs on teardown */
hidma_ll_setup_irq
(
dmadev
->
lldev
,
true
);
}
if
(
rc
)
dev_warn
(
&
pdev
->
dev
,
"failed to request MSI irq, falling back to wired IRQ
\n
"
);
return
rc
;
#else
return
-
EINVAL
;
#endif
}
static
bool
hidma_msi_capable
(
struct
device
*
dev
)
{
struct
acpi_device
*
adev
=
ACPI_COMPANION
(
dev
);
const
char
*
of_compat
;
int
ret
=
-
EINVAL
;
if
(
!
adev
||
acpi_disabled
)
{
ret
=
device_property_read_string
(
dev
,
"compatible"
,
&
of_compat
);
if
(
ret
)
return
false
;
ret
=
strcmp
(
of_compat
,
"qcom,hidma-1.1"
);
}
else
{
#ifdef CONFIG_ACPI
ret
=
strcmp
(
acpi_device_hid
(
adev
),
"QCOM8062"
);
#endif
}
return
ret
==
0
;
}
static
int
hidma_probe
(
struct
platform_device
*
pdev
)
...
...
@@ -599,6 +724,7 @@ static int hidma_probe(struct platform_device *pdev)
void
__iomem
*
evca
;
void
__iomem
*
trca
;
int
rc
;
bool
msi
;
pm_runtime_set_autosuspend_delay
(
&
pdev
->
dev
,
HIDMA_AUTOSUSPEND_TIMEOUT
);
pm_runtime_use_autosuspend
(
&
pdev
->
dev
);
...
...
@@ -660,6 +786,12 @@ static int hidma_probe(struct platform_device *pdev)
dmadev
->
ddev
.
device_terminate_all
=
hidma_terminate_all
;
dmadev
->
ddev
.
copy_align
=
8
;
/*
* Determine the MSI capability of the platform. Old HW doesn't
* support MSI.
*/
msi
=
hidma_msi_capable
(
&
pdev
->
dev
);
device_property_read_u32
(
&
pdev
->
dev
,
"desc-count"
,
&
dmadev
->
nr_descriptors
);
...
...
@@ -688,10 +820,17 @@ static int hidma_probe(struct platform_device *pdev)
goto
dmafree
;
}
rc
=
devm_request_irq
(
&
pdev
->
dev
,
chirq
,
hidma_chirq_handler
,
0
,
"qcom-hidma"
,
dmadev
->
lldev
);
if
(
rc
)
goto
uninit
;
platform_set_drvdata
(
pdev
,
dmadev
);
if
(
msi
)
rc
=
hidma_request_msi
(
dmadev
,
pdev
);
if
(
!
msi
||
rc
)
{
hidma_ll_setup_irq
(
dmadev
->
lldev
,
false
);
rc
=
devm_request_irq
(
&
pdev
->
dev
,
chirq
,
hidma_chirq_handler
,
0
,
"qcom-hidma"
,
dmadev
->
lldev
);
if
(
rc
)
goto
uninit
;
}
INIT_LIST_HEAD
(
&
dmadev
->
ddev
.
channels
);
rc
=
hidma_chan_init
(
dmadev
,
0
);
...
...
@@ -705,14 +844,16 @@ static int hidma_probe(struct platform_device *pdev)
dmadev
->
irq
=
chirq
;
tasklet_init
(
&
dmadev
->
task
,
hidma_issue_task
,
(
unsigned
long
)
dmadev
);
hidma_debug_init
(
dmadev
);
hidma_
create_sysfs_entry
(
dmadev
,
"chid"
,
S_IRUGO
);
hidma_
sysfs_init
(
dmadev
);
dev_info
(
&
pdev
->
dev
,
"HI-DMA engine driver registration complete
\n
"
);
platform_set_drvdata
(
pdev
,
dmadev
);
pm_runtime_mark_last_busy
(
dmadev
->
ddev
.
dev
);
pm_runtime_put_autosuspend
(
dmadev
->
ddev
.
dev
);
return
0
;
uninit:
if
(
msi
)
hidma_free_msis
(
dmadev
);
hidma_debug_uninit
(
dmadev
);
hidma_ll_uninit
(
dmadev
->
lldev
);
dmafree:
...
...
@@ -730,8 +871,13 @@ static int hidma_remove(struct platform_device *pdev)
pm_runtime_get_sync
(
dmadev
->
ddev
.
dev
);
dma_async_device_unregister
(
&
dmadev
->
ddev
);
devm_free_irq
(
dmadev
->
ddev
.
dev
,
dmadev
->
irq
,
dmadev
->
lldev
);
if
(
!
dmadev
->
lldev
->
msi_support
)
devm_free_irq
(
dmadev
->
ddev
.
dev
,
dmadev
->
irq
,
dmadev
->
lldev
);
else
hidma_free_msis
(
dmadev
);
tasklet_kill
(
&
dmadev
->
task
);
hidma_sysfs_uninit
(
dmadev
);
hidma_debug_uninit
(
dmadev
);
hidma_ll_uninit
(
dmadev
->
lldev
);
hidma_free
(
dmadev
);
...
...
@@ -746,12 +892,15 @@ static int hidma_remove(struct platform_device *pdev)
#if IS_ENABLED(CONFIG_ACPI)
static
const
struct
acpi_device_id
hidma_acpi_ids
[]
=
{
{
"QCOM8061"
},
{
"QCOM8062"
},
{},
};
MODULE_DEVICE_TABLE
(
acpi
,
hidma_acpi_ids
);
#endif
static
const
struct
of_device_id
hidma_match
[]
=
{
{.
compatible
=
"qcom,hidma-1.0"
,},
{.
compatible
=
"qcom,hidma-1.1"
,},
{},
};
MODULE_DEVICE_TABLE
(
of
,
hidma_match
);
...
...
drivers/dma/qcom/hidma.h
View file @
90644ad7
...
...
@@ -46,6 +46,7 @@ struct hidma_tre {
};
struct
hidma_lldev
{
bool
msi_support
;
/* flag indicating MSI support */
bool
initialized
;
/* initialized flag */
u8
trch_state
;
/* trch_state of the device */
u8
evch_state
;
/* evch_state of the device */
...
...
@@ -58,7 +59,7 @@ struct hidma_lldev {
void
__iomem
*
evca
;
/* Event Channel address */
struct
hidma_tre
**
pending_tre_list
;
/* Pointers to pending TREs */
s32
pending_tre_count
;
/* Number of TREs pending */
atomic_t
pending_tre_count
;
/* Number of TREs pending */
void
*
tre_ring
;
/* TRE ring */
dma_addr_t
tre_dma
;
/* TRE ring to be shared with HW */
...
...
@@ -114,6 +115,7 @@ struct hidma_dev {
int
irq
;
int
chidx
;
u32
nr_descriptors
;
int
msi_virqbase
;
struct
hidma_lldev
*
lldev
;
void
__iomem
*
dev_trca
;
...
...
@@ -128,6 +130,9 @@ struct hidma_dev {
struct
dentry
*
debugfs
;
struct
dentry
*
stats
;
/* sysfs entry for the channel id */
struct
device_attribute
*
chid_attrs
;
/* Task delivering issue_pending */
struct
tasklet_struct
task
;
};
...
...
@@ -145,12 +150,14 @@ int hidma_ll_disable(struct hidma_lldev *lldev);
int
hidma_ll_enable
(
struct
hidma_lldev
*
llhndl
);
void
hidma_ll_set_transfer_params
(
struct
hidma_lldev
*
llhndl
,
u32
tre_ch
,
dma_addr_t
src
,
dma_addr_t
dest
,
u32
len
,
u32
flags
);
void
hidma_ll_setup_irq
(
struct
hidma_lldev
*
lldev
,
bool
msi
);
int
hidma_ll_setup
(
struct
hidma_lldev
*
lldev
);
struct
hidma_lldev
*
hidma_ll_init
(
struct
device
*
dev
,
u32
max_channels
,
void
__iomem
*
trca
,
void
__iomem
*
evca
,
u8
chidx
);
int
hidma_ll_uninit
(
struct
hidma_lldev
*
llhndl
);
irqreturn_t
hidma_ll_inthandler
(
int
irq
,
void
*
arg
);
irqreturn_t
hidma_ll_inthandler_msi
(
int
irq
,
void
*
arg
,
int
cause
);
void
hidma_cleanup_pending_tre
(
struct
hidma_lldev
*
llhndl
,
u8
err_info
,
u8
err_code
);
int
hidma_debug_init
(
struct
hidma_dev
*
dmadev
);
...
...
drivers/dma/qcom/hidma_dbg.c
View file @
90644ad7
...
...
@@ -74,7 +74,8 @@ static void hidma_ll_devstats(struct seq_file *s, void *llhndl)
seq_printf
(
s
,
"tre_ring_handle=%pap
\n
"
,
&
lldev
->
tre_dma
);
seq_printf
(
s
,
"tre_ring_size = 0x%x
\n
"
,
lldev
->
tre_ring_size
);
seq_printf
(
s
,
"tre_processed_off = 0x%x
\n
"
,
lldev
->
tre_processed_off
);
seq_printf
(
s
,
"pending_tre_count=%d
\n
"
,
lldev
->
pending_tre_count
);
seq_printf
(
s
,
"pending_tre_count=%d
\n
"
,
atomic_read
(
&
lldev
->
pending_tre_count
));
seq_printf
(
s
,
"evca=%p
\n
"
,
lldev
->
evca
);
seq_printf
(
s
,
"evre_ring=%p
\n
"
,
lldev
->
evre_ring
);
seq_printf
(
s
,
"evre_ring_handle=%pap
\n
"
,
&
lldev
->
evre_dma
);
...
...
@@ -164,7 +165,6 @@ static const struct file_operations hidma_dma_fops = {
void
hidma_debug_uninit
(
struct
hidma_dev
*
dmadev
)
{
debugfs_remove_recursive
(
dmadev
->
debugfs
);
debugfs_remove_recursive
(
dmadev
->
stats
);
}
int
hidma_debug_init
(
struct
hidma_dev
*
dmadev
)
...
...
drivers/dma/qcom/hidma_ll.c
View file @
90644ad7
...
...
@@ -198,13 +198,16 @@ static void hidma_ll_tre_complete(unsigned long arg)
}
}
static
int
hidma_post_completed
(
struct
hidma_lldev
*
lldev
,
int
tre_iterator
,
u8
err_
info
,
u8
err_
code
)
static
int
hidma_post_completed
(
struct
hidma_lldev
*
lldev
,
u8
err_info
,
u8
err_code
)
{
struct
hidma_tre
*
tre
;
unsigned
long
flags
;
u32
tre_iterator
;
spin_lock_irqsave
(
&
lldev
->
lock
,
flags
);
tre_iterator
=
lldev
->
tre_processed_off
;
tre
=
lldev
->
pending_tre_list
[
tre_iterator
/
HIDMA_TRE_SIZE
];
if
(
!
tre
)
{
spin_unlock_irqrestore
(
&
lldev
->
lock
,
flags
);
...
...
@@ -218,12 +221,14 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
* Keep track of pending TREs that SW is expecting to receive
* from HW. We got one now. Decrement our counter.
*/
lldev
->
pending_tre_count
--
;
if
(
lldev
->
pending_tre_count
<
0
)
{
if
(
atomic_dec_return
(
&
lldev
->
pending_tre_count
)
<
0
)
{
dev_warn
(
lldev
->
dev
,
"tre count mismatch on completion"
);
lldev
->
pending_tre_count
=
0
;
atomic_set
(
&
lldev
->
pending_tre_count
,
0
)
;
}
HIDMA_INCREMENT_ITERATOR
(
tre_iterator
,
HIDMA_TRE_SIZE
,
lldev
->
tre_ring_size
);
lldev
->
tre_processed_off
=
tre_iterator
;
spin_unlock_irqrestore
(
&
lldev
->
lock
,
flags
);
tre
->
err_info
=
err_info
;
...
...
@@ -245,13 +250,11 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
static
int
hidma_handle_tre_completion
(
struct
hidma_lldev
*
lldev
)
{
u32
evre_ring_size
=
lldev
->
evre_ring_size
;
u32
tre_ring_size
=
lldev
->
tre_ring_size
;
u32
err_info
,
err_code
,
evre_write_off
;
u32
tre_iterator
,
evre_iterator
;
u32
evre_iterator
;
u32
num_completed
=
0
;
evre_write_off
=
readl_relaxed
(
lldev
->
evca
+
HIDMA_EVCA_WRITE_PTR_REG
);
tre_iterator
=
lldev
->
tre_processed_off
;
evre_iterator
=
lldev
->
evre_processed_off
;
if
((
evre_write_off
>
evre_ring_size
)
||
...
...
@@ -274,12 +277,9 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
err_code
=
(
cfg
>>
HIDMA_EVRE_CODE_BIT_POS
)
&
HIDMA_EVRE_CODE_MASK
;
if
(
hidma_post_completed
(
lldev
,
tre_iterator
,
err_info
,
err_code
))
if
(
hidma_post_completed
(
lldev
,
err_info
,
err_code
))
break
;
HIDMA_INCREMENT_ITERATOR
(
tre_iterator
,
HIDMA_TRE_SIZE
,
tre_ring_size
);
HIDMA_INCREMENT_ITERATOR
(
evre_iterator
,
HIDMA_EVRE_SIZE
,
evre_ring_size
);
...
...
@@ -291,21 +291,22 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
evre_write_off
=
readl_relaxed
(
lldev
->
evca
+
HIDMA_EVCA_WRITE_PTR_REG
);
num_completed
++
;
/*
* An error interrupt might have arrived while we are processing
* the completed interrupt.
*/
if
(
!
hidma_ll_isenabled
(
lldev
))
break
;
}
if
(
num_completed
)
{
u32
evre_read_off
=
(
lldev
->
evre_processed_off
+
HIDMA_EVRE_SIZE
*
num_completed
);
u32
tre_read_off
=
(
lldev
->
tre_processed_off
+
HIDMA_TRE_SIZE
*
num_completed
);
evre_read_off
=
evre_read_off
%
evre_ring_size
;
tre_read_off
=
tre_read_off
%
tre_ring_size
;
writel
(
evre_read_off
,
lldev
->
evca
+
HIDMA_EVCA_DOORBELL_REG
);
/* record the last processed tre offset */
lldev
->
tre_processed_off
=
tre_read_off
;
lldev
->
evre_processed_off
=
evre_read_off
;
}
...
...
@@ -315,27 +316,10 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
void
hidma_cleanup_pending_tre
(
struct
hidma_lldev
*
lldev
,
u8
err_info
,
u8
err_code
)
{
u32
tre_iterator
;
u32
tre_ring_size
=
lldev
->
tre_ring_size
;
int
num_completed
=
0
;
u32
tre_read_off
;
tre_iterator
=
lldev
->
tre_processed_off
;
while
(
lldev
->
pending_tre_count
)
{
if
(
hidma_post_completed
(
lldev
,
tre_iterator
,
err_info
,
err_code
))
while
(
atomic_read
(
&
lldev
->
pending_tre_count
))
{
if
(
hidma_post_completed
(
lldev
,
err_info
,
err_code
))
break
;
HIDMA_INCREMENT_ITERATOR
(
tre_iterator
,
HIDMA_TRE_SIZE
,
tre_ring_size
);
num_completed
++
;
}
tre_read_off
=
(
lldev
->
tre_processed_off
+
HIDMA_TRE_SIZE
*
num_completed
);
tre_read_off
=
tre_read_off
%
tre_ring_size
;
/* record the last processed tre offset */
lldev
->
tre_processed_off
=
tre_read_off
;
}
static
int
hidma_ll_reset
(
struct
hidma_lldev
*
lldev
)
...
...
@@ -412,12 +396,24 @@ static int hidma_ll_reset(struct hidma_lldev *lldev)
* requests traditionally to the destination, this concept does not apply
* here for this HW.
*/
irqreturn_t
hidma_ll_inthandler
(
int
chirq
,
void
*
arg
)
static
void
hidma_ll_int_handler_internal
(
struct
hidma_lldev
*
lldev
,
int
cause
)
{
struct
hidma_lldev
*
lldev
=
arg
;
u32
status
;
u32
enable
;
u32
cause
;
if
(
cause
&
HIDMA_ERR_INT_MASK
)
{
dev_err
(
lldev
->
dev
,
"error 0x%x, disabling...
\n
"
,
cause
);
/* Clear out pending interrupts */
writel
(
cause
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_CLR_REG
);
/* No further submissions. */
hidma_ll_disable
(
lldev
);
/* Driver completes the txn and intimates the client.*/
hidma_cleanup_pending_tre
(
lldev
,
0xFF
,
HIDMA_EVRE_STATUS_ERROR
);
return
;
}
/*
* Fine tuned for this HW...
...
...
@@ -426,35 +422,28 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
* read and write accessors are used for performance reasons due to
* interrupt delivery guarantees. Do not copy this code blindly and
* expect that to work.
*
* Try to consume as many EVREs as possible.
*/
hidma_handle_tre_completion
(
lldev
);
/* We consumed TREs or there are pending TREs or EVREs. */
writel_relaxed
(
cause
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_CLR_REG
);
}
irqreturn_t
hidma_ll_inthandler
(
int
chirq
,
void
*
arg
)
{
struct
hidma_lldev
*
lldev
=
arg
;
u32
status
;
u32
enable
;
u32
cause
;
status
=
readl_relaxed
(
lldev
->
evca
+
HIDMA_EVCA_IRQ_STAT_REG
);
enable
=
readl_relaxed
(
lldev
->
evca
+
HIDMA_EVCA_IRQ_EN_REG
);
cause
=
status
&
enable
;
while
(
cause
)
{
if
(
cause
&
HIDMA_ERR_INT_MASK
)
{
dev_err
(
lldev
->
dev
,
"error 0x%x, disabling...
\n
"
,
cause
);
/* Clear out pending interrupts */
writel
(
cause
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_CLR_REG
);
/* No further submissions. */
hidma_ll_disable
(
lldev
);
/* Driver completes the txn and intimates the client.*/
hidma_cleanup_pending_tre
(
lldev
,
0xFF
,
HIDMA_EVRE_STATUS_ERROR
);
goto
out
;
}
/*
* Try to consume as many EVREs as possible.
*/
hidma_handle_tre_completion
(
lldev
);
/* We consumed TREs or there are pending TREs or EVREs. */
writel_relaxed
(
cause
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_CLR_REG
);
hidma_ll_int_handler_internal
(
lldev
,
cause
);
/*
* Another interrupt might have arrived while we are
...
...
@@ -465,7 +454,14 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
cause
=
status
&
enable
;
}
out:
return
IRQ_HANDLED
;
}
irqreturn_t
hidma_ll_inthandler_msi
(
int
chirq
,
void
*
arg
,
int
cause
)
{
struct
hidma_lldev
*
lldev
=
arg
;
hidma_ll_int_handler_internal
(
lldev
,
cause
);
return
IRQ_HANDLED
;
}
...
...
@@ -548,7 +544,7 @@ void hidma_ll_queue_request(struct hidma_lldev *lldev, u32 tre_ch)
tre
->
err_code
=
0
;
tre
->
err_info
=
0
;
tre
->
queued
=
1
;
lldev
->
pending_tre_count
++
;
atomic_inc
(
&
lldev
->
pending_tre_count
)
;
lldev
->
tre_write_offset
=
(
lldev
->
tre_write_offset
+
HIDMA_TRE_SIZE
)
%
lldev
->
tre_ring_size
;
spin_unlock_irqrestore
(
&
lldev
->
lock
,
flags
);
...
...
@@ -564,19 +560,8 @@ int hidma_ll_disable(struct hidma_lldev *lldev)
u32
val
;
int
ret
;
val
=
readl
(
lldev
->
evca
+
HIDMA_EVCA_CTRLSTS_REG
);
lldev
->
evch_state
=
HIDMA_CH_STATE
(
val
);
val
=
readl
(
lldev
->
trca
+
HIDMA_TRCA_CTRLSTS_REG
);
lldev
->
trch_state
=
HIDMA_CH_STATE
(
val
);
/* already suspended by this OS */
if
((
lldev
->
trch_state
==
HIDMA_CH_SUSPENDED
)
||
(
lldev
->
evch_state
==
HIDMA_CH_SUSPENDED
))
return
0
;
/* already stopped by the manager */
if
((
lldev
->
trch_state
==
HIDMA_CH_STOPPED
)
||
(
lldev
->
evch_state
==
HIDMA_CH_STOPPED
))
/* The channel needs to be in working state */
if
(
!
hidma_ll_isenabled
(
lldev
))
return
0
;
val
=
readl
(
lldev
->
trca
+
HIDMA_TRCA_CTRLSTS_REG
);
...
...
@@ -654,7 +639,7 @@ int hidma_ll_setup(struct hidma_lldev *lldev)
u32
val
;
u32
nr_tres
=
lldev
->
nr_tres
;
lldev
->
pending_tre_count
=
0
;
atomic_set
(
&
lldev
->
pending_tre_count
,
0
)
;
lldev
->
tre_processed_off
=
0
;
lldev
->
evre_processed_off
=
0
;
lldev
->
tre_write_offset
=
0
;
...
...
@@ -691,17 +676,36 @@ int hidma_ll_setup(struct hidma_lldev *lldev)
writel
(
HIDMA_EVRE_SIZE
*
nr_tres
,
lldev
->
evca
+
HIDMA_EVCA_RING_LEN_REG
);
/* support IRQ only for now */
/* configure interrupts */
hidma_ll_setup_irq
(
lldev
,
lldev
->
msi_support
);
rc
=
hidma_ll_enable
(
lldev
);
if
(
rc
)
return
rc
;
return
rc
;
}
void
hidma_ll_setup_irq
(
struct
hidma_lldev
*
lldev
,
bool
msi
)
{
u32
val
;
lldev
->
msi_support
=
msi
;
/* disable interrupts again after reset */
writel
(
0
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_CLR_REG
);
writel
(
0
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_EN_REG
);
/* support IRQ by default */
val
=
readl
(
lldev
->
evca
+
HIDMA_EVCA_INTCTRL_REG
);
val
&=
~
0xF
;
val
|=
0x1
;
if
(
!
lldev
->
msi_support
)
val
=
val
|
0x1
;
writel
(
val
,
lldev
->
evca
+
HIDMA_EVCA_INTCTRL_REG
);
/* clear all pending interrupts and enable them */
writel
(
ENABLE_IRQS
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_CLR_REG
);
writel
(
ENABLE_IRQS
,
lldev
->
evca
+
HIDMA_EVCA_IRQ_EN_REG
);
return
hidma_ll_enable
(
lldev
);
}
struct
hidma_lldev
*
hidma_ll_init
(
struct
device
*
dev
,
u32
nr_tres
,
...
...
@@ -816,7 +820,7 @@ int hidma_ll_uninit(struct hidma_lldev *lldev)
tasklet_kill
(
&
lldev
->
task
);
memset
(
lldev
->
trepool
,
0
,
required_bytes
);
lldev
->
trepool
=
NULL
;
lldev
->
pending_tre_count
=
0
;
atomic_set
(
&
lldev
->
pending_tre_count
,
0
)
;
lldev
->
tre_write_offset
=
0
;
rc
=
hidma_ll_reset
(
lldev
);
...
...
drivers/dma/qcom/hidma_mgmt.c
View file @
90644ad7
...
...
@@ -282,6 +282,7 @@ static const struct acpi_device_id hidma_mgmt_acpi_ids[] = {
{
"QCOM8060"
},
{},
};
MODULE_DEVICE_TABLE
(
acpi
,
hidma_mgmt_acpi_ids
);
#endif
static
const
struct
of_device_id
hidma_mgmt_match
[]
=
{
...
...
@@ -375,8 +376,15 @@ static int __init hidma_mgmt_of_populate_channels(struct device_node *np)
ret
=
PTR_ERR
(
new_pdev
);
goto
out
;
}
of_node_get
(
child
);
new_pdev
->
dev
.
of_node
=
child
;
of_dma_configure
(
&
new_pdev
->
dev
,
child
);
/*
* It is assumed that calling of_msi_configure is safe on
* platforms with or without MSI support.
*/
of_msi_configure
(
&
new_pdev
->
dev
,
child
);
of_node_put
(
child
);
kfree
(
res
);
res
=
NULL
;
}
...
...
@@ -395,7 +403,6 @@ static int __init hidma_mgmt_init(void)
for_each_matching_node
(
child
,
hidma_mgmt_match
)
{
/* device tree based firmware here */
hidma_mgmt_of_populate_channels
(
child
);
of_node_put
(
child
);
}
#endif
platform_driver_register
(
&
hidma_mgmt_driver
);
...
...
drivers/of/irq.c
View file @
90644ad7
...
...
@@ -697,3 +697,4 @@ void of_msi_configure(struct device *dev, struct device_node *np)
dev_set_msi_domain
(
dev
,
of_msi_get_domain
(
dev
,
np
,
DOMAIN_BUS_PLATFORM_MSI
));
}
EXPORT_SYMBOL_GPL
(
of_msi_configure
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment