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