| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string.h> |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| |
| #include <ddk/driver.h> |
| #include <ddk/mmio-buffer.h> |
| |
| zx_status_t mmio_buffer_init(mmio_buffer_t* buffer, zx_off_t offset, size_t size, zx_handle_t vmo, |
| uint32_t cache_policy) { |
| zx_status_t status = zx_vmo_set_cache_policy(vmo, cache_policy); |
| if (status != ZX_OK) { |
| zx_handle_close(vmo); |
| return status; |
| } |
| |
| uintptr_t vaddr; |
| const size_t vmo_offset = ROUNDDOWN(offset, ZX_PAGE_SIZE); |
| const size_t page_offset = offset - vmo_offset; |
| const size_t vmo_size = ROUNDUP(size + page_offset, ZX_PAGE_SIZE); |
| |
| status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_MAP_RANGE, 0, |
| vmo, vmo_offset, vmo_size, &vaddr); |
| if (status != ZX_OK) { |
| zx_handle_close(vmo); |
| return status; |
| } |
| |
| buffer->vmo = vmo; |
| buffer->vaddr = (void*)(vaddr + page_offset); |
| buffer->offset = offset; |
| buffer->size = size; |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t mmio_buffer_init_physical(mmio_buffer_t* buffer, zx_paddr_t base, size_t size, |
| zx_handle_t resource, uint32_t cache_policy) { |
| zx_handle_t vmo; |
| zx_status_t status = zx_vmo_create_physical(resource, base, size, &vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // |base| is guaranteed to be page aligned. |
| return mmio_buffer_init(buffer, 0, size, vmo, cache_policy); |
| } |
| |
| void mmio_buffer_release(mmio_buffer_t* buffer) { |
| if (buffer->vmo != ZX_HANDLE_INVALID) { |
| zx_vmar_unmap(zx_vmar_root_self(), (uintptr_t)buffer->vaddr, buffer->size); |
| zx_handle_close(buffer->vmo); |
| buffer->vmo = ZX_HANDLE_INVALID; |
| } |
| } |
| |
| zx_status_t mmio_buffer_pin(mmio_buffer_t* buffer, zx_handle_t bti, mmio_pinned_buffer_t* out) { |
| zx_paddr_t paddr; |
| zx_handle_t pmt; |
| const uint32_t options = ZX_BTI_PERM_WRITE | ZX_BTI_PERM_READ | ZX_BTI_CONTIGUOUS; |
| const size_t vmo_offset = ROUNDDOWN(buffer->offset, ZX_PAGE_SIZE); |
| const size_t page_offset = buffer->offset - vmo_offset; |
| const size_t vmo_size = ROUNDUP(buffer->size + page_offset, ZX_PAGE_SIZE); |
| |
| zx_status_t status = zx_bti_pin(bti, options, buffer->vmo, vmo_offset, vmo_size, &paddr, 1, &pmt); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| out->mmio = buffer; |
| out->paddr = paddr + page_offset; |
| out->pmt = pmt; |
| |
| return ZX_OK; |
| } |
| |
| void mmio_buffer_unpin(mmio_pinned_buffer_t* buffer) { |
| if (buffer->pmt != ZX_HANDLE_INVALID) { |
| zx_pmt_unpin(buffer->pmt); |
| buffer->pmt = ZX_HANDLE_INVALID; |
| } |
| } |