summaryrefslogtreecommitdiffstats
path: root/drivers/firmware/tegra/ivc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware/tegra/ivc.c')
-rw-r--r--drivers/firmware/tegra/ivc.c150
1 files changed, 92 insertions, 58 deletions
diff --git a/drivers/firmware/tegra/ivc.c b/drivers/firmware/tegra/ivc.c
index e2398cd7ca98..8c9aff9804c0 100644
--- a/drivers/firmware/tegra/ivc.c
+++ b/drivers/firmware/tegra/ivc.c
@@ -68,6 +68,12 @@ struct tegra_ivc_header {
} rx;
};
+#define tegra_ivc_header_read_field(hdr, field) \
+ iosys_map_rd_field(hdr, 0, struct tegra_ivc_header, field)
+
+#define tegra_ivc_header_write_field(hdr, field, value) \
+ iosys_map_wr_field(hdr, 0, struct tegra_ivc_header, field, value)
+
static inline void tegra_ivc_invalidate(struct tegra_ivc *ivc, dma_addr_t phys)
{
if (!ivc->peer)
@@ -86,16 +92,15 @@ static inline void tegra_ivc_flush(struct tegra_ivc *ivc, dma_addr_t phys)
DMA_TO_DEVICE);
}
-static inline bool tegra_ivc_empty(struct tegra_ivc *ivc,
- struct tegra_ivc_header *header)
+static inline bool tegra_ivc_empty(struct tegra_ivc *ivc, struct iosys_map *map)
{
/*
* This function performs multiple checks on the same values with
* security implications, so create snapshots with READ_ONCE() to
* ensure that these checks use the same values.
*/
- u32 tx = READ_ONCE(header->tx.count);
- u32 rx = READ_ONCE(header->rx.count);
+ u32 tx = tegra_ivc_header_read_field(map, tx.count);
+ u32 rx = tegra_ivc_header_read_field(map, rx.count);
/*
* Perform an over-full check to prevent denial of service attacks
@@ -113,11 +118,10 @@ static inline bool tegra_ivc_empty(struct tegra_ivc *ivc,
return tx == rx;
}
-static inline bool tegra_ivc_full(struct tegra_ivc *ivc,
- struct tegra_ivc_header *header)
+static inline bool tegra_ivc_full(struct tegra_ivc *ivc, struct iosys_map *map)
{
- u32 tx = READ_ONCE(header->tx.count);
- u32 rx = READ_ONCE(header->rx.count);
+ u32 tx = tegra_ivc_header_read_field(map, tx.count);
+ u32 rx = tegra_ivc_header_read_field(map, rx.count);
/*
* Invalid cases where the counters indicate that the queue is over
@@ -126,11 +130,10 @@ static inline bool tegra_ivc_full(struct tegra_ivc *ivc,
return tx - rx >= ivc->num_frames;
}
-static inline u32 tegra_ivc_available(struct tegra_ivc *ivc,
- struct tegra_ivc_header *header)
+static inline u32 tegra_ivc_available(struct tegra_ivc *ivc, struct iosys_map *map)
{
- u32 tx = READ_ONCE(header->tx.count);
- u32 rx = READ_ONCE(header->rx.count);
+ u32 tx = tegra_ivc_header_read_field(map, tx.count);
+ u32 rx = tegra_ivc_header_read_field(map, rx.count);
/*
* This function isn't expected to be used in scenarios where an
@@ -143,8 +146,9 @@ static inline u32 tegra_ivc_available(struct tegra_ivc *ivc,
static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc)
{
- WRITE_ONCE(ivc->tx.channel->tx.count,
- READ_ONCE(ivc->tx.channel->tx.count) + 1);
+ unsigned int count = tegra_ivc_header_read_field(&ivc->tx.map, tx.count);
+
+ tegra_ivc_header_write_field(&ivc->tx.map, tx.count, count + 1);
if (ivc->tx.position == ivc->num_frames - 1)
ivc->tx.position = 0;
@@ -154,8 +158,9 @@ static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc)
static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc)
{
- WRITE_ONCE(ivc->rx.channel->rx.count,
- READ_ONCE(ivc->rx.channel->rx.count) + 1);
+ unsigned int count = tegra_ivc_header_read_field(&ivc->rx.map, rx.count);
+
+ tegra_ivc_header_write_field(&ivc->rx.map, rx.count, count + 1);
if (ivc->rx.position == ivc->num_frames - 1)
ivc->rx.position = 0;
@@ -166,6 +171,7 @@ static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc)
static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
{
unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
+ unsigned int state;
/*
* tx.channel->state is set locally, so it is not synchronized with
@@ -175,7 +181,8 @@ static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
* asynchronous transition of rx.channel->state to
* TEGRA_IVC_STATE_ACK is not allowed.
*/
- if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+ state = tegra_ivc_header_read_field(&ivc->tx.map, tx.state);
+ if (state != TEGRA_IVC_STATE_ESTABLISHED)
return -ECONNRESET;
/*
@@ -185,12 +192,12 @@ static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
* Synchronization is only necessary when these pointers indicate
* empty or full.
*/
- if (!tegra_ivc_empty(ivc, ivc->rx.channel))
+ if (!tegra_ivc_empty(ivc, &ivc->rx.map))
return 0;
tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
- if (tegra_ivc_empty(ivc, ivc->rx.channel))
+ if (tegra_ivc_empty(ivc, &ivc->rx.map))
return -ENOSPC;
return 0;
@@ -199,29 +206,34 @@ static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
static inline int tegra_ivc_check_write(struct tegra_ivc *ivc)
{
unsigned int offset = offsetof(struct tegra_ivc_header, rx.count);
+ unsigned int state;
- if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+ state = tegra_ivc_header_read_field(&ivc->tx.map, tx.state);
+ if (state != TEGRA_IVC_STATE_ESTABLISHED)
return -ECONNRESET;
- if (!tegra_ivc_full(ivc, ivc->tx.channel))
+ if (!tegra_ivc_full(ivc, &ivc->tx.map))
return 0;
tegra_ivc_invalidate(ivc, ivc->tx.phys + offset);
- if (tegra_ivc_full(ivc, ivc->tx.channel))
+ if (tegra_ivc_full(ivc, &ivc->tx.map))
return -ENOSPC;
return 0;
}
-static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
- struct tegra_ivc_header *header,
- unsigned int frame)
+static int tegra_ivc_frame_virt(struct tegra_ivc *ivc, const struct iosys_map *header,
+ unsigned int frame, struct iosys_map *map)
{
+ size_t offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
+
if (WARN_ON(frame >= ivc->num_frames))
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
- return (void *)(header + 1) + ivc->frame_size * frame;
+ *map = IOSYS_MAP_INIT_OFFSET(header, offset);
+
+ return 0;
}
static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
@@ -264,16 +276,16 @@ static inline void tegra_ivc_flush_frame(struct tegra_ivc *ivc,
}
/* directly peek at the next frame rx'ed */
-void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc)
+int tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc, struct iosys_map *map)
{
int err;
if (WARN_ON(ivc == NULL))
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
err = tegra_ivc_check_read(ivc);
if (err < 0)
- return ERR_PTR(err);
+ return err;
/*
* Order observation of ivc->rx.position potentially indicating new
@@ -284,7 +296,7 @@ void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc)
tegra_ivc_invalidate_frame(ivc, ivc->rx.phys, ivc->rx.position, 0,
ivc->frame_size);
- return tegra_ivc_frame_virt(ivc, ivc->rx.channel, ivc->rx.position);
+ return tegra_ivc_frame_virt(ivc, &ivc->rx.map, ivc->rx.position, map);
}
EXPORT_SYMBOL(tegra_ivc_read_get_next_frame);
@@ -320,7 +332,7 @@ int tegra_ivc_read_advance(struct tegra_ivc *ivc)
*/
tegra_ivc_invalidate(ivc, ivc->rx.phys + tx);
- if (tegra_ivc_available(ivc, ivc->rx.channel) == ivc->num_frames - 1)
+ if (tegra_ivc_available(ivc, &ivc->rx.map) == ivc->num_frames - 1)
ivc->notify(ivc, ivc->notify_data);
return 0;
@@ -328,15 +340,15 @@ int tegra_ivc_read_advance(struct tegra_ivc *ivc)
EXPORT_SYMBOL(tegra_ivc_read_advance);
/* directly poke at the next frame to be tx'ed */
-void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc)
+int tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc, struct iosys_map *map)
{
int err;
err = tegra_ivc_check_write(ivc);
if (err < 0)
- return ERR_PTR(err);
+ return err;
- return tegra_ivc_frame_virt(ivc, ivc->tx.channel, ivc->tx.position);
+ return tegra_ivc_frame_virt(ivc, &ivc->tx.map, ivc->tx.position, map);
}
EXPORT_SYMBOL(tegra_ivc_write_get_next_frame);
@@ -376,7 +388,7 @@ int tegra_ivc_write_advance(struct tegra_ivc *ivc)
*/
tegra_ivc_invalidate(ivc, ivc->tx.phys + rx);
- if (tegra_ivc_available(ivc, ivc->tx.channel) == 1)
+ if (tegra_ivc_available(ivc, &ivc->tx.map) == 1)
ivc->notify(ivc, ivc->notify_data);
return 0;
@@ -387,7 +399,7 @@ void tegra_ivc_reset(struct tegra_ivc *ivc)
{
unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
- ivc->tx.channel->tx.state = TEGRA_IVC_STATE_SYNC;
+ tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_SYNC);
tegra_ivc_flush(ivc, ivc->tx.phys + offset);
ivc->notify(ivc, ivc->notify_data);
}
@@ -416,13 +428,14 @@ EXPORT_SYMBOL(tegra_ivc_reset);
int tegra_ivc_notified(struct tegra_ivc *ivc)
{
unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
- enum tegra_ivc_state state;
+ enum tegra_ivc_state rx_state, tx_state;
/* Copy the receiver's state out of shared memory. */
tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
- state = READ_ONCE(ivc->rx.channel->tx.state);
+ rx_state = tegra_ivc_header_read_field(&ivc->rx.map, tx.state);
+ tx_state = tegra_ivc_header_read_field(&ivc->tx.map, tx.state);
- if (state == TEGRA_IVC_STATE_SYNC) {
+ if (rx_state == TEGRA_IVC_STATE_SYNC) {
offset = offsetof(struct tegra_ivc_header, tx.count);
/*
@@ -436,8 +449,8 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
* state and won't make progress until we change our state,
* so the counters are not in use at this time.
*/
- ivc->tx.channel->tx.count = 0;
- ivc->rx.channel->rx.count = 0;
+ tegra_ivc_header_write_field(&ivc->tx.map, tx.count, 0);
+ tegra_ivc_header_write_field(&ivc->rx.map, rx.count, 0);
ivc->tx.position = 0;
ivc->rx.position = 0;
@@ -452,7 +465,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
* Move to ACK state. We have just cleared our counters, so it
* is now safe for the remote end to start using these values.
*/
- ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ACK;
+ tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_ACK);
tegra_ivc_flush(ivc, ivc->tx.phys + offset);
/*
@@ -460,8 +473,8 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
*/
ivc->notify(ivc, ivc->notify_data);
- } else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_SYNC &&
- state == TEGRA_IVC_STATE_ACK) {
+ } else if (tx_state == TEGRA_IVC_STATE_SYNC &&
+ rx_state == TEGRA_IVC_STATE_ACK) {
offset = offsetof(struct tegra_ivc_header, tx.count);
/*
@@ -475,8 +488,8 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
* state and won't make progress until we change our state,
* so the counters are not in use at this time.
*/
- ivc->tx.channel->tx.count = 0;
- ivc->rx.channel->rx.count = 0;
+ tegra_ivc_header_write_field(&ivc->tx.map, tx.count, 0);
+ tegra_ivc_header_write_field(&ivc->rx.map, rx.count, 0);
ivc->tx.position = 0;
ivc->rx.position = 0;
@@ -492,7 +505,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
* already cleared its counters, so it is safe to start
* writing/reading on this channel.
*/
- ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
+ tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_ESTABLISHED);
tegra_ivc_flush(ivc, ivc->tx.phys + offset);
/*
@@ -500,7 +513,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
*/
ivc->notify(ivc, ivc->notify_data);
- } else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_ACK) {
+ } else if (tx_state == TEGRA_IVC_STATE_ACK) {
offset = offsetof(struct tegra_ivc_header, tx.count);
/*
@@ -516,7 +529,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
* cleared its counters, so it is safe to start writing/reading
* on this channel.
*/
- ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
+ tegra_ivc_header_write_field(&ivc->tx.map, tx.state, TEGRA_IVC_STATE_ESTABLISHED);
tegra_ivc_flush(ivc, ivc->tx.phys + offset);
/*
@@ -533,7 +546,7 @@ int tegra_ivc_notified(struct tegra_ivc *ivc)
*/
}
- if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+ if (tx_state != TEGRA_IVC_STATE_ESTABLISHED)
return -EAGAIN;
return 0;
@@ -609,8 +622,29 @@ static int tegra_ivc_check_params(unsigned long rx, unsigned long tx,
return 0;
}
-int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx,
- dma_addr_t rx_phys, void *tx, dma_addr_t tx_phys,
+static inline void iosys_map_copy(struct iosys_map *dst, const struct iosys_map *src)
+{
+ *dst = *src;
+}
+
+static inline unsigned long iosys_map_get_address(const struct iosys_map *map)
+{
+ if (map->is_iomem)
+ return (unsigned long)map->vaddr_iomem;
+
+ return (unsigned long)map->vaddr;
+}
+
+static inline void *iosys_map_get_vaddr(const struct iosys_map *map)
+{
+ if (WARN_ON(map->is_iomem))
+ return NULL;
+
+ return map->vaddr;
+}
+
+int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, const struct iosys_map *rx,
+ dma_addr_t rx_phys, const struct iosys_map *tx, dma_addr_t tx_phys,
unsigned int num_frames, size_t frame_size,
void (*notify)(struct tegra_ivc *ivc, void *data),
void *data)
@@ -628,7 +662,7 @@ int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx,
if (frame_size > INT_MAX)
return -E2BIG;
- err = tegra_ivc_check_params((unsigned long)rx, (unsigned long)tx,
+ err = tegra_ivc_check_params(iosys_map_get_address(rx), iosys_map_get_address(tx),
num_frames, frame_size);
if (err < 0)
return err;
@@ -636,12 +670,12 @@ int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx,
queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
if (peer) {
- ivc->rx.phys = dma_map_single(peer, rx, queue_size,
+ ivc->rx.phys = dma_map_single(peer, iosys_map_get_vaddr(rx), queue_size,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(peer, ivc->rx.phys))
return -ENOMEM;
- ivc->tx.phys = dma_map_single(peer, tx, queue_size,
+ ivc->tx.phys = dma_map_single(peer, iosys_map_get_vaddr(tx), queue_size,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(peer, ivc->tx.phys)) {
dma_unmap_single(peer, ivc->rx.phys, queue_size,
@@ -653,8 +687,8 @@ int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx,
ivc->tx.phys = tx_phys;
}
- ivc->rx.channel = rx;
- ivc->tx.channel = tx;
+ iosys_map_copy(&ivc->rx.map, rx);
+ iosys_map_copy(&ivc->tx.map, tx);
ivc->peer = peer;
ivc->notify = notify;
ivc->notify_data = data;