1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <unistd.h> 6#include <ddk/debug.h> 7#include <ddk/device.h> 8#include <ddk/io-buffer.h> 9#include <ddk/metadata.h> 10#include <ddk/protocol/gpio-impl.h> 11#include <ddk/protocol/platform-bus.h> 12#include <ddk/protocol/platform-defs.h> 13#include <ddk/protocol/serial.h> 14#include <hw/reg.h> 15#include <soc/aml-s905d2/s905d2-gpio.h> 16#include <soc/aml-s905d2/s905d2-hw.h> 17 18#include "astro.h" 19 20#define SOC_WIFI_LPO_32k768 S905D2_GPIOX(16) 21#define SOC_BT_REG_ON S905D2_GPIOX(17) 22 23static const pbus_mmio_t bt_uart_mmios[] = { 24 { 25 .base = S905D2_UART_A_BASE, 26 .length = S905D2_UART_A_LENGTH, 27 }, 28}; 29 30static const pbus_irq_t bt_uart_irqs[] = { 31 { 32 .irq = S905D2_UART_A_IRQ, 33 .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, 34 }, 35}; 36 37static const serial_port_info_t bt_uart_serial_info = { 38 .serial_class = SERIAL_CLASS_BLUETOOTH_HCI, 39 .serial_vid = PDEV_VID_BROADCOM, 40 .serial_pid = PDEV_PID_BCM43458, 41}; 42 43static const pbus_metadata_t bt_uart_metadata[] = { 44 { 45 .type = DEVICE_METADATA_SERIAL_PORT_INFO, 46 .data = &bt_uart_serial_info, 47 .len = sizeof(bt_uart_serial_info), 48 }, 49}; 50 51static pbus_dev_t bt_uart_dev = { 52 .name = "bt-uart", 53 .vid = PDEV_VID_AMLOGIC, 54 .pid = PDEV_PID_GENERIC, 55 .did = PDEV_DID_AMLOGIC_UART, 56 .mmios = bt_uart_mmios, 57 .mmio_count = countof(bt_uart_mmios), 58 .irqs = bt_uart_irqs, 59 .irq_count = countof(bt_uart_irqs), 60 .metadata = bt_uart_metadata, 61 .metadata_count = countof(bt_uart_metadata), 62}; 63 64// Enables and configures PWM_E on the SOC_WIFI_LPO_32k768 line for the Wifi/Bluetooth module 65static zx_status_t aml_enable_wifi_32K(aml_bus_t* bus) { 66 // Configure SOC_WIFI_LPO_32k768 pin for PWM_E 67 zx_status_t status = gpio_impl_set_alt_function(&bus->gpio, SOC_WIFI_LPO_32k768, 1); 68 if (status != ZX_OK) return status; 69 70 zx_handle_t bti; 71 status = iommu_get_bti(&bus->iommu, 0, BTI_BOARD, &bti); 72 if (status != ZX_OK) { 73 zxlogf(ERROR, "aml_enable_wifi_32K: iommu_get_bti failed: %d\n", status); 74 return status; 75 } 76 io_buffer_t buffer; 77 status = io_buffer_init_physical(&buffer, bti, S905D2_PWM_BASE, 0x1a000, get_root_resource(), 78 ZX_CACHE_POLICY_UNCACHED_DEVICE); 79 if (status != ZX_OK) { 80 zxlogf(ERROR, "aml_enable_wifi_32K: io_buffer_init_physical failed: %d\n", status); 81 zx_handle_close(bti); 82 return status; 83 } 84 uint32_t* regs = io_buffer_virt(&buffer); 85 86 // these magic numbers were gleaned by instrumenting drivers/amlogic/pwm/pwm_meson.c 87 // TODO(voydanoff) write a proper PWM driver 88 writel(0x016d016e, regs + S905D2_PWM_PWM_E); 89 writel(0x016d016d, regs + S905D2_PWM_E2); 90 writel(0x0a0a0609, regs + S905D2_PWM_TIME_EF); 91 writel(0x02808003, regs + S905D2_PWM_MISC_REG_EF); 92 93 io_buffer_release(&buffer); 94 zx_handle_close(bti); 95 96 return ZX_OK; 97} 98 99zx_status_t aml_bluetooth_init(aml_bus_t* bus) { 100 zx_status_t status; 101 102 // set alternate functions to enable Bluetooth UART 103 status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_TX_A, S905D2_UART_TX_A_FN); 104 if (status != ZX_OK) return status; 105 status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_RX_A, S905D2_UART_RX_A_FN); 106 if (status != ZX_OK) return status; 107 status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_CTS_A, S905D2_UART_CTS_A_FN); 108 if (status != ZX_OK) return status; 109 status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_RTS_A, S905D2_UART_RTS_A_FN); 110 if (status != ZX_OK) return status; 111 112 // Configure the SOC_WIFI_LPO_32k768 PWM, which is needed for the Bluetooth module to work properly 113 status = aml_enable_wifi_32K(bus); 114 if (status != ZX_OK) { 115 return status; 116 } 117 118 // set GPIO to reset Bluetooth module 119 gpio_impl_config_out(&bus->gpio, SOC_BT_REG_ON, 0); 120 usleep(10 * 1000); 121 gpio_impl_write(&bus->gpio, SOC_BT_REG_ON, 1); 122 usleep(100 * 1000); 123 124 // Bind UART for Bluetooth HCI 125 status = pbus_device_add(&bus->pbus, &bt_uart_dev); 126 if (status != ZX_OK) { 127 zxlogf(ERROR, "aml_uart_init: pbus_device_add failed: %d\n", status); 128 return status; 129 } 130 131 return ZX_OK; 132} 133