| // Copyright 2016 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 <assert.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <threads.h> |
| #include <unistd.h> |
| |
| #include <ddk/binding.h> |
| #include <ddk/debug.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/platform-defs.h> |
| #include <ddk/protocol/i2c.h> |
| #include <ddk/protocol/iommu.h> |
| #include <hw/reg.h> |
| #include <soc/hi3660/hi3660-hw.h> |
| #include <soc/hi3660/hi3660-regs.h> |
| |
| #include <zircon/assert.h> |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/threads.h> |
| |
| #include "hikey960.h" |
| #include "hikey960-hw.h" |
| |
| static zx_status_t hikey960_enable_ldo3(hikey960_t* hikey) { |
| writel(LDO3_ENABLE_BIT, hikey->pmu_ssio.vaddr + LDO3_ENABLE_REG); |
| return ZX_OK; |
| } |
| |
| static void hikey960_mmio_release(hikey960_t* hikey) { |
| mmio_buffer_release(&hikey->usb3otg_bc); |
| mmio_buffer_release(&hikey->peri_crg); |
| mmio_buffer_release(&hikey->pctrl); |
| mmio_buffer_release(&hikey->iomg_pmx4); |
| mmio_buffer_release(&hikey->pmu_ssio); |
| mmio_buffer_release(&hikey->iomcu); |
| mmio_buffer_release(&hikey->ufs_sctrl); |
| } |
| |
| static zx_status_t hikey960_init(hikey960_t* hikey, zx_handle_t resource) { |
| zx_status_t status; |
| if ((status = mmio_buffer_init_physical(&hikey->usb3otg_bc, MMIO_USB3OTG_BC_BASE, |
| MMIO_USB3OTG_BC_LENGTH, resource, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE)) != ZX_OK || |
| (status = mmio_buffer_init_physical(&hikey->peri_crg, MMIO_PERI_CRG_BASE, |
| MMIO_PERI_CRG_LENGTH, resource, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE)) != ZX_OK || |
| (status = mmio_buffer_init_physical(&hikey->pctrl, MMIO_PCTRL_BASE, MMIO_PCTRL_LENGTH, |
| resource, ZX_CACHE_POLICY_UNCACHED_DEVICE) != ZX_OK) || |
| (status = mmio_buffer_init_physical(&hikey->iomg_pmx4, MMIO_IOMG_PMX4_BASE, |
| MMIO_IOMG_PMX4_LENGTH, resource, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE)) != ZX_OK || |
| (status = mmio_buffer_init_physical(&hikey->pmu_ssio, MMIO_PMU_SSI0_BASE, |
| MMIO_PMU_SSI0_LENGTH, resource, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE)) != ZX_OK || |
| (status = mmio_buffer_init_physical(&hikey->iomcu, MMIO_IOMCU_CONFIG_BASE, |
| MMIO_IOMCU_CONFIG_LENGTH, resource, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE)) != ZX_OK || |
| (status = mmio_buffer_init_physical(&hikey->ufs_sctrl, MMIO_UFS_SYS_CTRL_BASE, |
| MMIO_UFS_SYS_CTRL_LENGTH, resource, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE)) != ZX_OK) { |
| goto fail; |
| } |
| |
| status = hikey960_ufs_init(hikey); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| status = hikey960_i2c1_init(hikey); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| status = hikey960_enable_ldo3(hikey); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| status = hikey960_i2c_pinmux(hikey); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| return ZX_OK; |
| |
| fail: |
| zxlogf(ERROR, "hikey960_init failed %d\n", status); |
| hikey960_mmio_release(hikey); |
| return status; |
| } |
| |
| static void hikey960_release(void* ctx) { |
| hikey960_t* hikey = ctx; |
| |
| hikey960_mmio_release(hikey); |
| zx_handle_close(hikey->bti_handle); |
| free(hikey); |
| } |
| |
| static zx_protocol_device_t hikey960_device_protocol = { |
| .version = DEVICE_OPS_VERSION, |
| .release = hikey960_release, |
| }; |
| |
| |
| static int hikey960_start_thread(void* arg) { |
| hikey960_t* hikey = arg; |
| zx_status_t status; |
| |
| status = hikey960_sysmem_init(hikey); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| status = hikey960_gpio_init(hikey); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| status = hikey960_i2c_init(hikey); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| // must be after hikey960_i2c_init |
| status = hikey960_dsi_init(hikey); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "hikey960_dsi_init failed\n"); |
| } |
| |
| if ((status = hikey960_add_devices(hikey)) != ZX_OK) { |
| zxlogf(ERROR, "hikey960_bind: hikey960_add_devices failed!\n");; |
| } |
| |
| return ZX_OK; |
| |
| fail: |
| zxlogf(ERROR, "hikey960_start_thread failed, not all devices have been initialized\n"); |
| return status; |
| } |
| |
| static zx_status_t hikey960_bind(void* ctx, zx_device_t* parent) { |
| hikey960_t* hikey = calloc(1, sizeof(hikey960_t)); |
| if (!hikey) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PBUS, &hikey->pbus); |
| if (status != ZX_OK) { |
| free(hikey); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| // get dummy IOMMU implementation in the platform bus |
| iommu_protocol_t iommu; |
| status = device_get_protocol(parent, ZX_PROTOCOL_IOMMU, &iommu); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "hikey960_bind: could not get ZX_PROTOCOL_IOMMU\n"); |
| goto fail; |
| } |
| status = iommu_get_bti(&iommu, 0, BTI_BOARD, &hikey->bti_handle); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "hikey960_bind: iommu_get_bti failed: %d\n", status); |
| return status; |
| } |
| |
| hikey->parent = parent; |
| |
| // TODO(voydanoff) get from platform bus driver somehow |
| // Please do not use get_root_resource() in new code. See ZX-1497. |
| zx_handle_t resource = get_root_resource(); |
| status = hikey960_init(hikey, resource); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "hikey960_bind: hikey960_init failed %d\n", status); |
| goto fail; |
| } |
| |
| device_add_args_t args = { |
| .version = DEVICE_ADD_ARGS_VERSION, |
| .name = "hikey960", |
| .ctx = hikey, |
| .ops = &hikey960_device_protocol, |
| // nothing should bind to this device |
| // all interaction will be done via the pbus_interface_protocol_t |
| .flags = DEVICE_ADD_NON_BINDABLE, |
| }; |
| |
| status = device_add(parent, &args, NULL); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| thrd_t t; |
| int thrd_rc = thrd_create_with_name(&t, hikey960_start_thread, hikey, "hikey960_start_thread"); |
| if (thrd_rc != thrd_success) { |
| status = thrd_status_to_zx_status(thrd_rc); |
| goto fail; |
| } |
| return ZX_OK; |
| |
| fail: |
| zxlogf(ERROR, "hikey960_bind failed %d\n", status); |
| hikey960_release(hikey); |
| return status; |
| } |
| |
| static zx_driver_ops_t hikey960_driver_ops = { |
| .version = DRIVER_OPS_VERSION, |
| .bind = hikey960_bind, |
| }; |
| |
| ZIRCON_DRIVER_BEGIN(hikey960, hikey960_driver_ops, "zircon", "0.1", 3) |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PBUS), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_96BOARDS), |
| BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_HIKEY960), |
| ZIRCON_DRIVER_END(hikey960) |