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/mmio-buffer.h>
9 #include <ddk/metadata.h>
10 #include <ddk/platform-defs.h>
11 #include <ddk/protocol/gpioimpl.h>
12 #include <ddk/protocol/platform/bus.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 #include <zircon/device/serial.h>
18 
19 #include "astro.h"
20 
21 #define SOC_WIFI_LPO_32k768 S905D2_GPIOX(16)
22 #define SOC_BT_REG_ON       S905D2_GPIOX(17)
23 
24 static const pbus_mmio_t bt_uart_mmios[] = {
25     {
26         .base = S905D2_UART_A_BASE,
27         .length = S905D2_UART_A_LENGTH,
28     },
29 };
30 
31 static const pbus_irq_t bt_uart_irqs[] = {
32     {
33         .irq = S905D2_UART_A_IRQ,
34         .mode = ZX_INTERRUPT_MODE_EDGE_HIGH,
35     },
36 };
37 
38 static const serial_port_info_t bt_uart_serial_info = {
39     .serial_class = SERIAL_CLASS_BLUETOOTH_HCI,
40     .serial_vid = PDEV_VID_BROADCOM,
41     .serial_pid = PDEV_PID_BCM43458,
42 };
43 
44 static const pbus_metadata_t bt_uart_metadata[] = {
45     {
46         .type = DEVICE_METADATA_SERIAL_PORT_INFO,
47         .data_buffer = &bt_uart_serial_info,
48         .data_size = sizeof(bt_uart_serial_info),
49     },
50 };
51 
52 static const pbus_boot_metadata_t bt_uart_boot_metadata[] = {
53     {
54         .zbi_type = DEVICE_METADATA_MAC_ADDRESS,
55         .zbi_extra = MACADDR_BLUETOOTH,
56     },
57 };
58 
59 static pbus_dev_t bt_uart_dev = {
60     .name = "bt-uart",
61     .vid = PDEV_VID_AMLOGIC,
62     .pid = PDEV_PID_GENERIC,
63     .did = PDEV_DID_AMLOGIC_UART,
64     .mmio_list = bt_uart_mmios,
65     .mmio_count = countof(bt_uart_mmios),
66     .irq_list = bt_uart_irqs,
67     .irq_count = countof(bt_uart_irqs),
68     .metadata_list = bt_uart_metadata,
69     .metadata_count = countof(bt_uart_metadata),
70     .boot_metadata_list = bt_uart_boot_metadata,
71     .boot_metadata_count = countof(bt_uart_boot_metadata),
72 };
73 
74 // Enables and configures PWM_E on the SOC_WIFI_LPO_32k768 line for the Wifi/Bluetooth module
aml_enable_wifi_32K(aml_bus_t * bus)75 static zx_status_t aml_enable_wifi_32K(aml_bus_t* bus) {
76     // Configure SOC_WIFI_LPO_32k768 pin for PWM_E
77     zx_status_t status = gpio_impl_set_alt_function(&bus->gpio, SOC_WIFI_LPO_32k768, 1);
78     if (status != ZX_OK) return status;
79 
80     zx_handle_t bti;
81     status = iommu_get_bti(&bus->iommu, 0, BTI_BOARD, &bti);
82     if (status != ZX_OK) {
83         zxlogf(ERROR, "aml_enable_wifi_32K: iommu_get_bti failed: %d\n", status);
84         return status;
85     }
86     mmio_buffer_t buffer;
87     status = mmio_buffer_init_physical(&buffer, S905D2_PWM_BASE, 0x1a000, get_root_resource(),
88                                        ZX_CACHE_POLICY_UNCACHED_DEVICE);
89     if (status != ZX_OK) {
90         zxlogf(ERROR, "aml_enable_wifi_32K: mmio_buffer_init_physical failed: %d\n", status);
91         zx_handle_close(bti);
92         return status;
93     }
94     uint32_t* regs = buffer.vaddr;
95 
96     // these magic numbers were gleaned by instrumenting drivers/amlogic/pwm/pwm_meson.c
97     // TODO(voydanoff) write a proper PWM driver
98     writel(0x016d016e, regs + S905D2_PWM_PWM_E);
99     writel(0x016d016d, regs + S905D2_PWM_E2);
100     writel(0x0a0a0609, regs + S905D2_PWM_TIME_EF);
101     writel(0x02808003, regs + S905D2_PWM_MISC_REG_EF);
102 
103     mmio_buffer_release(&buffer);
104     zx_handle_close(bti);
105 
106     return ZX_OK;
107 }
108 
aml_bluetooth_init(aml_bus_t * bus)109 zx_status_t aml_bluetooth_init(aml_bus_t* bus) {
110     zx_status_t status;
111 
112     // set alternate functions to enable Bluetooth UART
113     status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_TX_A, S905D2_UART_TX_A_FN);
114     if (status != ZX_OK) return status;
115     status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_RX_A, S905D2_UART_RX_A_FN);
116     if (status != ZX_OK) return status;
117     status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_CTS_A, S905D2_UART_CTS_A_FN);
118     if (status != ZX_OK) return status;
119     status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_RTS_A, S905D2_UART_RTS_A_FN);
120     if (status != ZX_OK) return status;
121 
122     // Configure the SOC_WIFI_LPO_32k768 PWM, which is needed for the Bluetooth module to work properly
123     status = aml_enable_wifi_32K(bus);
124      if (status != ZX_OK) {
125         return status;
126     }
127 
128     // set GPIO to reset Bluetooth module
129     gpio_impl_config_out(&bus->gpio, SOC_BT_REG_ON, 0);
130     usleep(10 * 1000);
131     gpio_impl_write(&bus->gpio, SOC_BT_REG_ON, 1);
132     usleep(100 * 1000);
133 
134     // Bind UART for Bluetooth HCI
135     status = pbus_device_add(&bus->pbus, &bt_uart_dev);
136     if (status != ZX_OK) {
137         zxlogf(ERROR, "aml_uart_init: pbus_device_add failed: %d\n", status);
138         return status;
139     }
140 
141     return ZX_OK;
142 }
143