F: drivers/amlogic/spicc/spicc.h
F: drivers/amlogic/spicc/Kconfig
F: drivers/amlogic/spicc/Makefile
+
+AMLOGIC BT/WIFI
+M: Rongjun Chen <rongjun.chen@amlogic.com>
+F: drivers/amlogic/bluetooth/
+F: drivers/amlogic/wifi/
+
+
};
};
+ wifi{
+ compatible = "amlogic, aml_wifi";
+ dev_name = "aml_wifi";
+ status = "okay";
+ interrupt_pin = <&gpio GPIOX_7 GPIO_ACTIVE_HIGH>;
+ interrupts = < 0 68 4>;
+ irq_trigger_type = "GPIO_IRQ_LOW";
+ power_on_pin = <&gpio GPIOX_6 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "wifi_32k_pins";
+ pinctrl-0 = <&wifi_32k_pins>;
+// pwm_config = <&wifi_pwm_conf>;
+ };
+
+// wifi_pwm_conf:wifi_pwm_conf{
+// pwm_channel1 = <PWM_E>;
+// pwm_channel2 = <PWM_E2>;
+// pwm_channel1_conf = <30541 15270 6>;
+// pwm_channel2_conf = <30518 15259 9>;
+// };
+
sd_emmc_c: emmc@d0074000 {
status = "okay";
compatible = "amlogic, meson-aml-mmc";
};
};
+ wifi{
+ compatible = "amlogic, aml_wifi";
+ dev_name = "aml_wifi";
+ status = "okay";
+ interrupt_pin = <&gpio GPIOX_7 GPIO_ACTIVE_HIGH>;
+ interrupts = < 0 68 4>;
+ irq_trigger_type = "GPIO_IRQ_LOW";
+ power_on_pin = <&gpio GPIOX_6 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "wifi_32k_pins";
+ pinctrl-0 = <&wifi_32k_pins>;
+// pwm_config = <&wifi_pwm_conf>;
+ };
+
+// wifi_pwm_conf:wifi_pwm_conf{
+// pwm_channel1 = <PWM_E>;
+// pwm_channel2 = <PWM_E2>;
+// pwm_channel1_conf = <30541 15270 6>;
+// pwm_channel2_conf = <30518 15259 9>;
+// };
+
sd_emmc_c: emmc@d0074000 {
status = "okay";
compatible = "amlogic, meson-aml-mmc";
};
};
+ wifi{
+ compatible = "amlogic, aml_wifi";
+ dev_name = "aml_wifi";
+ status = "okay";
+ interrupt_pin = <&gpio GPIOX_7 GPIO_ACTIVE_HIGH>;
+ interrupts = < 0 68 4>;
+ irq_trigger_type = "GPIO_IRQ_LOW";
+ power_on_pin = <&gpio GPIOX_6 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "wifi_32k_pins";
+ pinctrl-0 = <&wifi_32k_pins>;
+ };
+
sd_emmc_c: emmc@d0074000 {
status = "okay";
compatible = "amlogic, meson-aml-mmc";
};
};
+ wifi{
+ compatible = "amlogic, aml_wifi";
+ dev_name = "aml_wifi";
+ status = "okay";
+ interrupt_pin = <&gpio GPIOX_7 GPIO_ACTIVE_HIGH>;
+ interrupts = < 0 68 4>;
+ irq_trigger_type = "GPIO_IRQ_LOW";
+ power_on_pin = <&gpio GPIOX_6 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "wifi_32k_pins";
+ pinctrl-0 = <&wifi_32k_pins>;
+ };
+
sd_emmc_c: emmc@d0074000 {
status = "disabled";
compatible = "amlogic, meson-aml-mmc";
};
};
+ wifi{
+ compatible = "amlogic, aml_wifi";
+ dev_name = "aml_wifi";
+ status = "okay";
+ interrupt_pin = <&gpio GPIOX_7 GPIO_ACTIVE_HIGH>;
+ interrupts = < 0 68 4>;
+ irq_trigger_type = "GPIO_IRQ_LOW";
+ power_on_pin = <&gpio GPIOX_6 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "wifi_32k_pins";
+ pinctrl-0 = <&wifi_32k_pins>;
+ };
+
sd_emmc_c: emmc@d0074000 {
status = "okay";
compatible = "amlogic, meson-aml-mmc";
CONFIG_AMLOGIC_JTAG_MESON=y
CONFIG_AMLOGIC_WDT=y
CONFIG_AMLOGIC_WDT_MESON=y
+CONFIG_AMLOGIC_WIFI=y
+CONFIG_AMLOGIC_BT_DEVICE=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
source "drivers/amlogic/watchdog/Kconfig"
+source "drivers/amlogic/wifi/Kconfig"
+
+source "drivers/amlogic/bluetooth/Kconfig"
+
endmenu
endif
obj-$(CONFIG_AMLOGIC_WDT) += watchdog/
+obj-$(CONFIG_AMLOGIC_BT_DEVICE) += bluetooth/
+
+obj-$(CONFIG_AMLOGIC_WIFI) += wifi/
+
--- /dev/null
+comment "Amlogic Bt Rfkill Driver"
+
+menuconfig AMLOGIC_BT_DEVICE
+ bool "BT Device support"
+ depends on BT && RFKILL
+ default n
+ ---help---
+ Say Y here if you want to use the BT device.
--- /dev/null
+obj-$(CONFIG_AMLOGIC_BT_DEVICE) += bt_device.o
--- /dev/null
+/*
+ * Date Author Comment
+ * ----------- -------------- --------------------------------
+ * 2006-Apr-28 Motorola The kernel module for running the Bluetooth(R)
+ * Sleep-Mode Protocol from the Host side
+ * 2006-Sep-08 Motorola Added workqueue for handling sleep work.
+ * 2007-Jan-24 Motorola Added mbm_handle_ioi() call to ISR.
+ * 2009-Aug-10 Motorola Changed "add_timer" to "mod_timer" to solve
+ * race when flurry of queued work comes in.
+ */
+
+#define pr_fmt(fmt) "Bluetooth: %s: " fmt, __func__
+
+#include <linux/module.h> /* kernel module definitions */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/irq.h>
+#include <linux/ioport.h>
+#include <linux/param.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <linux/wakelock_android.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/serial_core.h>
+
+#include <linux/of_irq.h>
+/* #include <linux/platform_data/msm_serial_hs.h> */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h> /* event notifications */
+#include "../../bluetooth/hci_uart.h"
+#include "bluesleep.h"
+
+#define BT_SLEEP_DBG
+#ifndef BT_SLEEP_DBG
+#define BT_DBG(fmt, arg...)
+#endif
+/*
+ * Defines
+ */
+
+#define VERSION "1.2"
+#define PROC_DIR "bluetooth/sleep"
+#define BT_SLEEP "bluesleep"
+
+#define POLARITY_LOW 0
+#define POLARITY_HIGH 1
+
+#define BT_PORT_ID 0
+
+/* enable/disable wake-on-bluetooth */
+#define BT_ENABLE_IRQ_WAKE 0
+
+#define BT_BLUEDROID_SUPPORT 1
+
+enum {
+ DEBUG_USER_STATE = 1U << 0,
+ DEBUG_SUSPEND = 1U << 1,
+ DEBUG_BTWAKE = 1U << 2,
+ DEBUG_VERBOSE = 1U << 3,
+};
+
+static int debug_mask = DEBUG_USER_STATE;
+//module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+module_param_named(debug_mask, debug_mask, int, 0664);
+
+struct bluesleep_info {
+ unsigned int host_wake;
+ unsigned int ext_wake;
+ unsigned int host_wake_irq;
+ struct uart_port *uport;
+ struct wake_lock wake_lock;
+ int irq_polarity;
+ int has_ext_wake;
+};
+
+/* work function */
+static void bluesleep_sleep_work(struct work_struct *work);
+
+/* work queue */
+DECLARE_DELAYED_WORK(sleep_workqueue, bluesleep_sleep_work);
+
+/* Macros for handling sleep work */
+#define bluesleep_rx_busy() schedule_delayed_work(&sleep_workqueue, 0)
+#define bluesleep_tx_busy() schedule_delayed_work(&sleep_workqueue, 0)
+#define bluesleep_rx_idle() schedule_delayed_work(&sleep_workqueue, 0)
+#define bluesleep_tx_idle() schedule_delayed_work(&sleep_workqueue, 0)
+
+/* 5 second timeout */
+#define TX_TIMER_INTERVAL 5
+
+/* state variable names and bit positions */
+#define BT_PROTO 0x01
+#define BT_TXDATA 0x02
+#define BT_ASLEEP 0x04
+#define BT_EXT_WAKE 0x08
+#define BT_SUSPEND 0x10
+
+#define PROC_BTWAKE 0
+#define PROC_HOSTWAKE 1
+#define PROC_PROTO 2
+#define PROC_ASLEEP 3
+#if BT_BLUEDROID_SUPPORT
+#define PROC_LPM 4
+#define PROC_BTWRITE 5
+#endif
+
+#if BT_BLUEDROID_SUPPORT
+static bool has_lpm_enabled;
+#else
+/* global pointer to a single hci device. */
+static struct hci_dev *bluesleep_hdev;
+#endif
+
+static struct platform_device *bluesleep_uart_dev;
+static struct bluesleep_info *bsi;
+static int bt_port_id = 1;
+
+/* module usage */
+static atomic_t open_count = ATOMIC_INIT(1);
+
+/*
+ * Local function prototypes
+ */
+#if !BT_BLUEDROID_SUPPORT
+static int bluesleep_hci_event(struct notifier_block *this,
+ unsigned long event, void *data);
+#endif
+static int bluesleep_start(void);
+static void bluesleep_stop(void);
+
+/*
+ * Global variables
+ */
+
+/** Global state flags */
+static unsigned long flags;
+
+/** Tasklet to respond to change in hostwake line */
+static struct tasklet_struct hostwake_task;
+
+/** Transmission timer */
+static void bluesleep_tx_timer_expire(unsigned long data);
+static DEFINE_TIMER(tx_timer, bluesleep_tx_timer_expire, 0, 0);
+
+/** Lock for state transitions */
+static spinlock_t rw_lock;
+
+#if !BT_BLUEDROID_SUPPORT
+/** Notifier block for HCI events */
+struct notifier_block hci_event_nblock = {
+ .notifier_call = bluesleep_hci_event,
+};
+#endif
+
+struct proc_dir_entry *bluetooth_dir, *sleep_dir;
+
+/*
+ * Local functions
+ */
+
+static void hsuart_power(int on)
+{
+}
+
+/**
+ * @return 1 if the Host can go to sleep, 0 otherwise.
+ */
+int bluesleep_can_sleep(void)
+{
+ /* check if WAKE_BT_GPIO and BT_WAKE_GPIO are both deasserted */
+ return (gpio_get_value(bsi->host_wake) != bsi->irq_polarity) &&
+ (test_bit(BT_EXT_WAKE, &flags)) &&
+ (bsi->uport != NULL);
+}
+
+void bluesleep_sleep_wakeup(void)
+{
+ if (test_bit(BT_ASLEEP, &flags)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("waking up...\n");
+ wake_lock(&bsi->wake_lock);
+ /* Start the timer */
+ mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ));
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to wake\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 1);
+ clear_bit(BT_EXT_WAKE, &flags);
+ clear_bit(BT_ASLEEP, &flags);
+ /*Activating UART */
+ hsuart_power(1);
+ }
+}
+
+/**
+ * @brief@ main sleep work handling function which update the flags
+ * and activate and deactivate UART ,check FIFO.
+ */
+static void bluesleep_sleep_work(struct work_struct *work)
+{
+ if (bluesleep_can_sleep()) {
+ /* already asleep, this is an error case */
+ if (test_bit(BT_ASLEEP, &flags)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("already asleep\n");
+ return;
+ }
+
+ if (bsi->uport->ops->tx_empty(bsi->uport)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("going to sleep...\n");
+ set_bit(BT_ASLEEP, &flags);
+ /*Deactivating UART */
+ hsuart_power(0);
+ /* UART clk is not turned off immediately. Release
+ * wakelock after 500 ms.
+ */
+ wake_lock_timeout(&bsi->wake_lock, HZ / 2);
+ } else {
+
+ mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ));
+ return;
+ }
+ } else if (test_bit(BT_EXT_WAKE, &flags)
+ && !test_bit(BT_ASLEEP, &flags)) {
+ mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ));
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to wake\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 1);
+ clear_bit(BT_EXT_WAKE, &flags);
+ } else {
+ bluesleep_sleep_wakeup();
+ }
+}
+
+/**
+ * A tasklet function that runs in tasklet context and reads the value
+ * of the HOST_WAKE GPIO pin and further defer the work.
+ * @param data Not used.
+ */
+static void bluesleep_hostwake_task(unsigned long data)
+{
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("hostwake line change\n");
+
+ spin_lock(&rw_lock);
+ if (gpio_get_value(bsi->host_wake) == bsi->irq_polarity)
+ bluesleep_rx_busy();
+ else
+ bluesleep_rx_idle();
+
+ spin_unlock(&rw_lock);
+}
+
+/**
+ * Handles proper timer action when outgoing data is delivered to the
+ * HCI line discipline. Sets BT_TXDATA.
+ */
+static void bluesleep_outgoing_data(void)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&rw_lock, irq_flags);
+
+ /* log data passing by */
+ set_bit(BT_TXDATA, &flags);
+
+ spin_unlock_irqrestore(&rw_lock, irq_flags);
+
+ /* if the tx side is sleeping... */
+ if (test_bit(BT_EXT_WAKE, &flags)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("tx was sleeping\n");
+ bluesleep_sleep_wakeup();
+ }
+}
+
+
+#if !BT_BLUEDROID_SUPPORT
+/**
+ * Handles HCI device events.
+ * @param this Not used.
+ * @param event The event that occurred.
+ * @param data The HCI device associated with the event.
+ * @return <code>NOTIFY_DONE</code>.
+ */
+static int bluesleep_hci_event(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ struct hci_dev *hdev = (struct hci_dev *) data;
+ struct hci_uart *hu;
+ struct uart_state *state;
+
+ if (!hdev)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case HCI_DEV_REG:
+ if (!bluesleep_hdev) {
+ bluesleep_hdev = hdev;
+ hu = (struct hci_uart *) hdev->driver_data;
+ state = (struct uart_state *) hu->tty->driver_data;
+ bsi->uport = state->uart_port;
+ /* if bluetooth started, start bluesleep*/
+ bluesleep_start();
+ }
+ break;
+ case HCI_DEV_UNREG:
+ bluesleep_stop();
+ bluesleep_hdev = NULL;
+ bsi->uport = NULL;
+ /* if bluetooth stopped, stop bluesleep also */
+ break;
+ case HCI_DEV_WRITE:
+ bluesleep_outgoing_data();
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+#endif
+
+/**
+ * Handles transmission timer expiration.
+ * @param data Not used.
+ */
+static void bluesleep_tx_timer_expire(unsigned long data)
+{
+ unsigned long irq_flags;
+
+ if (debug_mask & DEBUG_VERBOSE)
+ pr_info("Tx timer expired\n");
+
+ spin_lock_irqsave(&rw_lock, irq_flags);
+
+ /* were we silent during the last timeout? */
+ if (!test_bit(BT_TXDATA, &flags)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("Tx has been idle\n");
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to sleep\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 0);
+ set_bit(BT_EXT_WAKE, &flags);
+ bluesleep_tx_idle();
+ } else {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("Tx data during last period\n");
+ mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL*HZ));
+ }
+
+ /* clear the incoming data flag */
+ clear_bit(BT_TXDATA, &flags);
+
+ spin_unlock_irqrestore(&rw_lock, irq_flags);
+}
+
+/**
+ * Schedules a tasklet to run when receiving an interrupt on the
+ * <code>HOST_WAKE</code> GPIO pin.
+ * @param irq Not used.
+ * @param dev_id Not used.
+ */
+static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id)
+{
+ /* schedule a tasklet to handle the change in the host wake line */
+ tasklet_schedule(&hostwake_task);
+ return IRQ_HANDLED;
+}
+
+/**
+ * Starts the Sleep-Mode Protocol on the Host.
+ * @return On success, 0. On error, -1, and <code>errno</code> is set
+ * appropriately.
+ */
+static int bluesleep_start(void)
+{
+ int retval;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&rw_lock, irq_flags);
+
+ if (test_bit(BT_PROTO, &flags)) {
+ spin_unlock_irqrestore(&rw_lock, irq_flags);
+ return 0;
+ }
+
+ spin_unlock_irqrestore(&rw_lock, irq_flags);
+
+ if (!atomic_dec_and_test(&open_count)) {
+ atomic_inc(&open_count);
+ return -EBUSY;
+ }
+
+ /* start the timer */
+
+ mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL*HZ));
+
+ /* assert BT_WAKE */
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to wake\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 1);
+ clear_bit(BT_EXT_WAKE, &flags);
+ retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
+ IRQF_DISABLED,
+ "bluetooth hostwake", NULL);
+ if (retval < 0) {
+ BT_ERR("Couldn't acquire BT_HOST_WAKE IRQ");
+ goto fail;
+ }
+#if BT_ENABLE_IRQ_WAKE
+ retval = enable_irq_wake(bsi->host_wake_irq);
+ if (retval < 0) {
+ BT_ERR("Couldn't enable BT_HOST_WAKE as wakeup interrupt");
+ goto fail;
+ }
+#endif
+ set_bit(BT_PROTO, &flags);
+ wake_lock(&bsi->wake_lock);
+ return 0;
+
+fail:
+ del_timer(&tx_timer);
+ atomic_inc(&open_count);
+
+ return retval;
+}
+
+/**
+ * Stops the Sleep-Mode Protocol on the Host.
+ */
+static void bluesleep_stop(void)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&rw_lock, irq_flags);
+
+ if (!test_bit(BT_PROTO, &flags)) {
+ spin_unlock_irqrestore(&rw_lock, irq_flags);
+ return;
+ }
+
+ /* assert BT_WAKE */
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to wake\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 1);
+ clear_bit(BT_EXT_WAKE, &flags);
+ del_timer(&tx_timer);
+ clear_bit(BT_PROTO, &flags);
+
+ if (test_bit(BT_ASLEEP, &flags)) {
+ clear_bit(BT_ASLEEP, &flags);
+ spin_unlock_irqrestore(&rw_lock, irq_flags);
+ hsuart_power(1);
+ } else {
+ spin_unlock_irqrestore(&rw_lock, irq_flags);
+ }
+
+ atomic_inc(&open_count);
+
+#if BT_ENABLE_IRQ_WAKE
+ if (disable_irq_wake(bsi->host_wake_irq))
+ BT_ERR("Couldn't disable hostwake IRQ wakeup mode");
+#endif
+ free_irq(bsi->host_wake_irq, NULL);
+ wake_lock_timeout(&bsi->wake_lock, HZ / 2);
+}
+
+void bluesleep_setup_uart_port(struct platform_device *uart_dev)
+{
+ bluesleep_uart_dev = uart_dev;
+}
+
+static int bluesleep_populate_dt_pinfo(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int tmp;
+ const void *prop;
+
+ tmp = of_get_named_gpio(np, "bt_host_wake", 0);
+ if (tmp < 0) {
+ BT_ERR("couldn't find host_wake gpio");
+ return -ENODEV;
+ }
+ bsi->host_wake = tmp;
+
+ tmp = of_get_named_gpio(np, "bt_ext_wake", 0);
+ if (tmp < 0)
+ bsi->has_ext_wake = 0;
+ else
+ bsi->has_ext_wake = 1;
+
+ if (bsi->has_ext_wake)
+ bsi->ext_wake = tmp;
+
+ prop = of_get_property(np, "bt_port_id", NULL);
+ if (prop) {
+ bt_port_id = of_read_ulong(prop, 1);
+ BT_INFO("bt port id is %d\n", bt_port_id);
+ }
+
+ BT_INFO("bt_host_wake %d, bt_ext_wake %d",
+ bsi->host_wake,
+ bsi->ext_wake);
+ return 0;
+}
+
+static int bluesleep_populate_pinfo(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "gpio_host_wake");
+ if (!res) {
+ BT_ERR("couldn't find host_wake gpio");
+ return -ENODEV;
+ }
+ bsi->host_wake = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "gpio_ext_wake");
+ if (!res)
+ bsi->has_ext_wake = 0;
+ else
+ bsi->has_ext_wake = 1;
+
+ if (bsi->has_ext_wake)
+ bsi->ext_wake = res->start;
+
+ return 0;
+}
+
+static int bluesleep_probe(struct platform_device *pdev)
+{
+ /* struct resource *res; */
+ int ret;
+
+ bsi = kzalloc(sizeof(struct bluesleep_info), GFP_KERNEL);
+ if (!bsi)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ ret = bluesleep_populate_dt_pinfo(pdev);
+ if (ret < 0) {
+ BT_ERR("couldn't populate info from dt");
+ return ret;
+ }
+ } else {
+ ret = bluesleep_populate_pinfo(pdev);
+ if (ret < 0) {
+ BT_ERR("couldn't populate info");
+ return ret;
+ }
+ }
+
+ /* configure host_wake as input */
+ ret = gpio_request_one(bsi->host_wake, GPIOF_IN, "bt_host_wake");
+ if (ret < 0) {
+ BT_ERR("failed to configure input direction for GPIO %d err %d",
+ bsi->host_wake, ret);
+ goto free_bsi;
+ }
+
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to wake\n");
+ if (bsi->has_ext_wake) {
+ /* configure ext_wake as output mode*/
+ ret = gpio_request_one(bsi->ext_wake,
+ GPIOF_OUT_INIT_LOW, "bt_ext_wake");
+ if (ret < 0) {
+ BT_ERR("failed to config output");
+ BT_ERR(" for GPIO %d err %d",
+ bsi->ext_wake, ret);
+ goto free_bt_host_wake;
+ }
+ }
+ clear_bit(BT_EXT_WAKE, &flags);
+
+ bsi->host_wake_irq =
+ irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (bsi->host_wake_irq < 0) {
+ BT_ERR("couldn't find host_wake irq");
+ ret = -ENODEV;
+ goto free_bt_ext_wake;
+ }
+
+ gpio_for_irq(bsi->host_wake,
+ AML_GPIO_IRQ(bsi->host_wake_irq, FILTER_NUM7,
+ GPIO_IRQ_FALLING));
+
+ bsi->irq_polarity = POLARITY_LOW;/*low edge (falling edge)*/
+
+ wake_lock_init(&bsi->wake_lock,
+ WAKE_LOCK_SUSPEND, "bluesleep");
+ clear_bit(BT_SUSPEND, &flags);
+
+ BT_INFO("host_wake_irq %d, polarity %d",
+ bsi->host_wake_irq,
+ bsi->irq_polarity);
+
+ return 0;
+
+free_bt_ext_wake:
+ gpio_free(bsi->ext_wake);
+free_bt_host_wake:
+ gpio_free(bsi->host_wake);
+free_bsi:
+ kfree(bsi);
+ return ret;
+}
+
+static int bluesleep_remove(struct platform_device *pdev)
+{
+ gpio_free(bsi->host_wake);
+ gpio_free(bsi->ext_wake);
+ wake_lock_destroy(&bsi->wake_lock);
+ kfree(bsi);
+ return 0;
+}
+
+
+static int bluesleep_resume(struct platform_device *pdev)
+{
+ if (test_bit(BT_SUSPEND, &flags)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("bluesleep resuming...\n");
+ if ((bsi->uport != NULL) &&
+ (gpio_get_value(bsi->host_wake) == bsi->irq_polarity)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("bluesleep resume from BT event...\n");
+ }
+ clear_bit(BT_SUSPEND, &flags);
+ }
+ return 0;
+}
+
+static int bluesleep_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("bluesleep suspending...\n");
+ set_bit(BT_SUSPEND, &flags);
+ return 0;
+}
+
+static const struct of_device_id bluesleep_match_table[] = {
+ { .compatible = "amlogic, bluesleep" },
+ {}
+};
+
+static struct platform_driver bluesleep_driver = {
+ .probe = bluesleep_probe,
+ .remove = bluesleep_remove,
+ .suspend = bluesleep_suspend,
+ .resume = bluesleep_resume,
+ .driver = {
+ .name = "bluesleep",
+ .owner = THIS_MODULE,
+ .of_match_table = bluesleep_match_table,
+ },
+};
+
+static int bluesleep_proc_show(struct seq_file *m, void *v)
+{
+ switch ((long)m->private) {
+ case PROC_BTWAKE:
+ seq_printf(m, "btwake: %u\n", test_bit(BT_EXT_WAKE, &flags));
+ break;
+ case PROC_HOSTWAKE:
+ seq_printf(m, "hostwake: %u\n", gpio_get_value(bsi->host_wake));
+ break;
+ case PROC_PROTO:
+ seq_printf(m, "proto: %u\n",
+ test_bit(BT_PROTO, &flags) ? 1 : 0);
+ break;
+ case PROC_ASLEEP:
+ seq_printf(m, "asleep: %u\n",
+ test_bit(BT_ASLEEP, &flags) ? 1 : 0);
+ break;
+ case PROC_LPM:
+ seq_printf(m, "lpm: %u\n",
+ has_lpm_enabled ? 1 : 0);
+ break;
+ case PROC_BTWRITE:
+ seq_printf(m, "lpm: %u\n",
+ test_bit(BT_TXDATA, &flags) ? 1 : 0);
+ break;
+
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static ssize_t bluesleep_proc_write(struct file *file, const char *buf,
+ size_t count, loff_t *pos)
+{
+ void *data = PDE_DATA(file_inode(file));
+ char lbuf[32];
+
+ if (count >= sizeof(lbuf))
+ count = sizeof(lbuf)-1;
+
+ if (copy_from_user(lbuf, buf, count))
+ return -EFAULT;
+ lbuf[count] = 0;
+
+ switch ((long)data) {
+ case PROC_BTWAKE:
+ if (lbuf[0] == '0') {
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to wake\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 1);
+ clear_bit(BT_EXT_WAKE, &flags);
+ } else if (buf[0] == '1') {
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to sleep\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 0);
+ set_bit(BT_EXT_WAKE, &flags);
+ }
+ break;
+ case PROC_PROTO:
+ if (lbuf[0] == '0')
+ bluesleep_stop();
+ else
+ bluesleep_start();
+ break;
+ case PROC_LPM:
+ if (lbuf[0] == '0') {
+ bluesleep_stop();
+ has_lpm_enabled = false;
+ bsi->uport = NULL;
+ } else {
+ if (!has_lpm_enabled) {
+ has_lpm_enabled = true;
+ bsi->uport = get_uart_port(bt_port_id);
+ if (bsi->uport)
+ bluesleep_start();
+ else
+ pr_info("bt port is null\n");
+ }
+ }
+ break;
+ case PROC_BTWRITE:
+ if (lbuf[0] != '0' && bsi->uport)
+ bluesleep_outgoing_data();
+ break;
+ default:
+ return 0;
+ }
+
+ return count;
+}
+
+static int bluesleep_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bluesleep_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations bluesleep_proc_readwrite_fops = {
+ .owner = THIS_MODULE,
+ .open = bluesleep_proc_open,
+ .read = seq_read,
+ .write = bluesleep_proc_write,
+};
+static const struct file_operations bluesleep_proc_read_fops = {
+ .owner = THIS_MODULE,
+ .open = bluesleep_proc_open,
+ .read = seq_read,
+};
+
+/**
+ * Initializes the module.
+ * @return On success, 0. On error, -1, and <code>errno</code> is set
+ * appropriately.
+ */
+static int __init bluesleep_init(void)
+{
+ int retval;
+ struct proc_dir_entry *ent;
+
+ BT_INFO("BlueSleep Mode Driver Ver %s", VERSION);
+
+ retval = platform_driver_register(&bluesleep_driver);
+ if (retval)
+ return retval;
+
+ if (bsi == NULL)
+ return 0;
+
+#if !BT_BLUEDROID_SUPPORT
+ bluesleep_hdev = NULL;
+#endif
+
+ bluetooth_dir = proc_mkdir("bluetooth", NULL);
+ if (bluetooth_dir == NULL) {
+ BT_ERR("Unable to create /proc/bluetooth directory");
+ return -ENOMEM;
+ }
+
+ sleep_dir = proc_mkdir("sleep", bluetooth_dir);
+ if (sleep_dir == NULL) {
+ BT_ERR("Unable to create /proc/%s directory", PROC_DIR);
+ return -ENOMEM;
+ }
+
+ ent = proc_create_data("btwake", 0664,
+ sleep_dir, &bluesleep_proc_readwrite_fops,
+ (void *)PROC_BTWAKE);
+ if (ent == NULL) {
+ BT_ERR("Unable to create /proc/%s/btwake entry", PROC_DIR);
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ ent = proc_create_data("hostwake", 0444, sleep_dir,
+ &bluesleep_proc_read_fops,
+ (void *)PROC_HOSTWAKE);
+ if (ent == NULL) {
+ BT_ERR("Unable to create /proc/%s/hostwake entry", PROC_DIR);
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ ent = proc_create_data("proto", 0664,
+ sleep_dir, &bluesleep_proc_readwrite_fops,
+ (void *)PROC_PROTO);
+ if (ent == NULL) {
+ BT_ERR("Unable to create /proc/%s/proto entry", PROC_DIR);
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ ent = proc_create_data("asleep", 0444,
+ sleep_dir, &bluesleep_proc_read_fops,
+ (void *)PROC_ASLEEP);
+ if (ent == NULL) {
+ BT_ERR("Unable to create /proc/%s/asleep entry", PROC_DIR);
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+#if BT_BLUEDROID_SUPPORT
+ ent = proc_create_data("lpm", 0664,
+ sleep_dir, &bluesleep_proc_readwrite_fops,
+ (void *)PROC_LPM);
+ if (ent == NULL) {
+ BT_ERR("Unable to create /proc/%s/lpm entry", PROC_DIR);
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ ent = proc_create_data("btwrite", 0664,
+ sleep_dir, &bluesleep_proc_readwrite_fops,
+ (void *)PROC_BTWRITE);
+ if (ent == NULL) {
+ BT_ERR("Unable to create /proc/%s/btwrite entry", PROC_DIR);
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+#endif
+
+ flags = 0;
+
+ spin_lock_init(&rw_lock);
+
+ init_timer(&tx_timer);
+ tx_timer.function = bluesleep_tx_timer_expire;
+ tx_timer.data = 0;
+
+ tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0);
+
+ if (debug_mask & DEBUG_BTWAKE)
+ pr_info("BT WAKE: set to wake\n");
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 1);
+ clear_bit(BT_EXT_WAKE, &flags);
+#if !BT_BLUEDROID_SUPPORT
+ hci_register_notifier(&hci_event_nblock);
+#endif
+
+ return 0;
+
+fail:
+#if BT_BLUEDROID_SUPPORT
+ remove_proc_entry("btwrite", sleep_dir);
+ remove_proc_entry("lpm", sleep_dir);
+#endif
+ remove_proc_entry("asleep", sleep_dir);
+ remove_proc_entry("proto", sleep_dir);
+ remove_proc_entry("hostwake", sleep_dir);
+ remove_proc_entry("btwake", sleep_dir);
+ remove_proc_entry("sleep", bluetooth_dir);
+ remove_proc_entry("bluetooth", 0);
+ return retval;
+}
+
+/**
+ * Cleans up the module.
+ */
+static void __exit bluesleep_exit(void)
+{
+ if (bsi == NULL)
+ return;
+
+ if (bsi->has_ext_wake == 1)
+ gpio_set_value(bsi->ext_wake, 1);
+ clear_bit(BT_EXT_WAKE, &flags);
+ if (test_bit(BT_PROTO, &flags)) {
+ if (disable_irq_wake(bsi->host_wake_irq))
+ BT_ERR("Couldn't disable hostwake IRQ wakeup mode");
+ free_irq(bsi->host_wake_irq, NULL);
+ del_timer(&tx_timer);
+ if (test_bit(BT_ASLEEP, &flags))
+ hsuart_power(1);
+ }
+
+#if !BT_BLUEDROID_SUPPORT
+ hci_unregister_notifier(&hci_event_nblock);
+#endif
+ platform_driver_unregister(&bluesleep_driver);
+
+#if BT_BLUEDROID_SUPPORT
+ remove_proc_entry("btwrite", sleep_dir);
+ remove_proc_entry("lpm", sleep_dir);
+#endif
+ remove_proc_entry("asleep", sleep_dir);
+ remove_proc_entry("proto", sleep_dir);
+ remove_proc_entry("hostwake", sleep_dir);
+ remove_proc_entry("btwake", sleep_dir);
+ remove_proc_entry("sleep", bluetooth_dir);
+ remove_proc_entry("bluetooth", 0);
+}
+
+module_init(bluesleep_init);
+module_exit(bluesleep_exit);
+
+MODULE_DESCRIPTION("Bluetooth Sleep Mode Driver ver %s " VERSION);
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
--- /dev/null
+/*
+ * drivers/amlogic/bluetooth/bluesleep.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _BLUESLEEP_H
+#define _BLUESLEEP_H
+#include <linux/serial_core.h>
+extern struct uart_port *get_uart_port(int id);
+#endif
--- /dev/null
+/*
+ * drivers/amlogic/bluetooth/bt_device.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/rfkill.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/iomap.h>
+#include <linux/io.h>
+#include <linux/amlogic/bt_device.h>
+#ifdef CONFIG_AM_WIFI_SD_MMC
+#include <linux/amlogic/wifi_dt.h>
+#endif
+#include "../../gpio/gpiolib.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+static struct early_suspend bt_early_suspend;
+#endif
+
+#define BT_RFKILL "bt_rfkill"
+
+struct bt_dev_runtime_data {
+ struct rfkill *bt_rfk;
+ struct bt_dev_data *pdata;
+};
+
+static void bt_device_init(struct bt_dev_data *pdata)
+{
+ if (pdata->gpio_reset > 0)
+ gpio_request(pdata->gpio_reset, BT_RFKILL);
+
+ if (pdata->gpio_en > 0)
+ gpio_request(pdata->gpio_en, BT_RFKILL);
+
+}
+
+static void bt_device_deinit(struct bt_dev_data *pdata)
+{
+ if (pdata->gpio_reset > 0)
+ gpio_free(pdata->gpio_reset);
+
+ if (pdata->gpio_en > 0)
+ gpio_free(pdata->gpio_en);
+
+}
+
+static void bt_device_on(struct bt_dev_data *pdata)
+{
+ if (pdata->gpio_reset > 0) {
+
+ if ((pdata->power_on_pin_OD) && (pdata->power_low_level)) {
+ gpio_direction_input(pdata->gpio_reset);
+ } else {
+ gpio_direction_output(pdata->gpio_reset,
+ pdata->power_low_level);
+ }
+ }
+ if (pdata->gpio_en > 0) {
+
+ if ((pdata->power_on_pin_OD)
+ && (pdata->power_low_level)) {
+ gpio_direction_input(pdata->gpio_en);
+ } else {
+ gpio_direction_output(pdata->gpio_en,
+ pdata->power_low_level);
+ }
+ }
+ msleep(200);
+ if (pdata->gpio_reset > 0) {
+
+ if ((pdata->power_on_pin_OD)
+ && (!pdata->power_low_level)) {
+ gpio_direction_input(pdata->gpio_reset);
+ } else {
+ gpio_direction_output(pdata->gpio_reset,
+ !pdata->power_low_level);
+ }
+ }
+ if (pdata->gpio_en > 0) {
+
+ if ((pdata->power_on_pin_OD)
+ && (!pdata->power_low_level)) {
+ gpio_direction_input(pdata->gpio_en);
+ } else {
+ gpio_direction_output(pdata->gpio_en,
+ !pdata->power_low_level);
+ }
+ }
+ msleep(200);
+}
+
+static void bt_device_off(struct bt_dev_data *pdata)
+{
+ if (pdata->gpio_reset > 0) {
+
+ if ((pdata->power_on_pin_OD)
+ && (pdata->power_low_level)) {
+ gpio_direction_input(pdata->gpio_reset);
+ } else {
+ gpio_direction_output(pdata->gpio_reset,
+ pdata->power_low_level);
+ }
+ }
+ if (pdata->gpio_en > 0) {
+
+ if ((pdata->power_on_pin_OD)
+ && (pdata->power_low_level)) {
+ gpio_direction_input(pdata->gpio_en);
+ } else {
+ gpio_direction_output(pdata->gpio_en,
+ pdata->power_low_level);
+ }
+ }
+ msleep(20);
+}
+
+static int bt_set_block(void *data, bool blocked)
+{
+ struct bt_dev_data *pdata = data;
+
+ pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
+
+ if (!blocked) {
+ pr_info("BCM_BT: going ON\n");
+ bt_device_on(pdata);
+ } else {
+ pr_info("BCM_BT: going OFF\n");
+ bt_device_off(pdata);
+ }
+ return 0;
+}
+
+static const struct rfkill_ops bt_rfkill_ops = {
+ .set_block = bt_set_block,
+};
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bt_earlysuspend(struct early_suspend *h)
+{
+
+}
+
+static void bt_lateresume(struct early_suspend *h)
+{
+}
+#endif
+
+static int bt_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+
+ return 0;
+}
+
+static int bt_resume(struct platform_device *pdev)
+{
+
+ return 0;
+}
+
+static int bt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ const void *prop;
+ struct rfkill *bt_rfk;
+ struct bt_dev_data *pdata = NULL;
+ struct bt_dev_runtime_data *prdata;
+
+#ifdef CONFIG_OF
+ if (pdev->dev.of_node) {
+ const char *str;
+ struct gpio_desc *desc;
+
+ pr_info("enter bt_probe of_node\n");
+ pdata = kzalloc(sizeof(struct bt_dev_data), GFP_KERNEL);
+ ret = of_property_read_string(pdev->dev.of_node,
+ "gpio_reset", &str);
+ if (ret) {
+ pr_warn("not get gpio_reset\n");
+ pdata->gpio_reset = 0;
+ } else {
+ desc = of_get_named_gpiod_flags(pdev->dev.of_node,
+ "gpio_reset", 0, NULL);
+ pdata->gpio_reset = desc_to_gpio(desc);
+ }
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "gpio_en", &str);
+ if (ret) {
+ pr_warn("not get gpio_en\n");
+ pdata->gpio_en = 0;
+ } else {
+ desc = of_get_named_gpiod_flags(pdev->dev.of_node,
+ "gpio_en", 0, NULL);
+ pdata->gpio_en = desc_to_gpio(desc);
+ }
+
+ prop = of_get_property(pdev->dev.of_node,
+ "power_low_level", NULL);
+ if (prop) {
+ pr_info("power on valid level is low");
+ pdata->power_low_level = 1;
+ } else {
+ pr_info("power on valid level is high");
+ pdata->power_low_level = 0;
+ pdata->power_on_pin_OD = 0;
+ }
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "power_on_pin_OD", &pdata->power_on_pin_OD);
+ if (ret)
+ pdata->power_on_pin_OD = 0;
+ pr_info("bt: power_on_pin_OD = %d;\n", pdata->power_on_pin_OD);
+ }
+#else
+ pdata = (struct bt_dev_data *)(pdev->dev.platform_data);
+#endif
+
+ bt_device_init(pdata);
+ /* default to bluetooth off */
+ /* rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, 1); */
+ /* bt_device_off(pdata); */
+
+ bt_rfk = rfkill_alloc("bt-dev", &pdev->dev,
+ RFKILL_TYPE_BLUETOOTH,
+ &bt_rfkill_ops, pdata);
+
+ if (!bt_rfk) {
+ pr_info("rfk alloc fail\n");
+ ret = -ENOMEM;
+ goto err_rfk_alloc;
+ }
+
+ rfkill_init_sw_state(bt_rfk, false);
+ ret = rfkill_register(bt_rfk);
+ if (ret) {
+ pr_err("rfkill_register fail\n");
+ goto err_rfkill;
+ }
+ prdata = kmalloc(sizeof(struct bt_dev_runtime_data),
+ GFP_KERNEL);
+
+ if (!prdata)
+ goto err_rfkill;
+
+ prdata->bt_rfk = bt_rfk;
+ prdata->pdata = pdata;
+ platform_set_drvdata(pdev, prdata);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ bt_early_suspend.level =
+ EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ bt_early_suspend.suspend = bt_earlysuspend;
+ bt_early_suspend.resume = bt_lateresume;
+ bt_early_suspend.param = pdev;
+ register_early_suspend(&bt_early_suspend);
+#endif
+
+ return 0;
+
+err_rfkill:
+ rfkill_destroy(bt_rfk);
+err_rfk_alloc:
+ bt_device_deinit(pdata);
+ return ret;
+
+}
+
+static int bt_remove(struct platform_device *pdev)
+{
+ struct bt_dev_runtime_data *prdata =
+ platform_get_drvdata(pdev);
+ struct rfkill *rfk = NULL;
+ struct bt_dev_data *pdata = NULL;
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (prdata) {
+ rfk = prdata->bt_rfk;
+ pdata = prdata->pdata;
+ }
+
+ if (pdata) {
+ bt_device_deinit(pdata);
+ kfree(pdata);
+ }
+
+ if (rfk) {
+ rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ }
+ rfk = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id bt_dev_dt_match[] = {
+ { .compatible = "amlogic, bt-dev",
+ },
+ {},
+};
+#else
+#define bt_dev_dt_match NULL
+#endif
+
+static struct platform_driver bt_driver = {
+ .driver = {
+ .name = "bt-dev",
+ .of_match_table = bt_dev_dt_match,
+ },
+ .probe = bt_probe,
+ .remove = bt_remove,
+ .suspend = bt_suspend,
+ .resume = bt_resume,
+};
+
+static int __init bt_init(void)
+{
+ pr_info("amlogic rfkill init\n");
+
+ return platform_driver_register(&bt_driver);
+}
+static void __exit bt_exit(void)
+{
+ platform_driver_unregister(&bt_driver);
+}
+
+module_init(bt_init);
+module_exit(bt_exit);
+MODULE_DESCRIPTION("bt rfkill");
+MODULE_AUTHOR("");
+MODULE_LICENSE("GPL");
--- /dev/null
+comment "Amlogic Wifi Driver"
+
+menuconfig AMLOGIC_WIFI
+ bool "WiFi support"
+ default n
+ help
+ select y to support wifi
--- /dev/null
+obj-$(CONFIG_AMLOGIC_WIFI) += wifi_dt.o
--- /dev/null
+/*
+ * drivers/amlogic/wifi/dhd_static_buf.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#define pr_fmt(fmt) "Wifi: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/skbuff.h>
+#include <linux/wlan_plat.h>
+#include <linux/amlogic/dhd_buf.h>
+
+#define CONFIG_BROADCOM_WIFI_RESERVED_MEM
+
+#ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM
+
+#define WLAN_STATIC_PKT_BUF 4
+#define WLAN_STATIC_SCAN_BUF0 5
+#define WLAN_STATIC_SCAN_BUF1 6
+#define WLAN_STATIC_DHD_INFO 7
+#define WLAN_STATIC_DHD_WLFC_INFO 8
+#define PREALLOC_WLAN_SEC_NUM 6
+#define PREALLOC_WLAN_BUF_NUM 160
+#define PREALLOC_WLAN_SECTION_HEADER 24
+
+#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_BUF_NUM * 128)
+#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_BUF_NUM * 128)
+#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_BUF_NUM * 512)
+#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_BUF_NUM * 1024)
+#define WLAN_SECTION_SIZE_7 (PREALLOC_WLAN_BUF_NUM * 128)
+#define WLAN_SECTION_SIZE_8 (PREALLOC_WLAN_BUF_NUM * 512)
+
+#define DHD_SKB_HDRSIZE 336
+#define DHD_SKB_1PAGE_BUFSIZE ((PAGE_SIZE*1)-DHD_SKB_HDRSIZE)
+#define DHD_SKB_2PAGE_BUFSIZE ((PAGE_SIZE*2)-DHD_SKB_HDRSIZE)
+#define DHD_SKB_4PAGE_BUFSIZE ((PAGE_SIZE*4)-DHD_SKB_HDRSIZE)
+
+#define WLAN_SKB_BUF_NUM 17
+
+static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
+
+struct wlan_mem_prealloc {
+ void *mem_ptr;
+ unsigned long size;
+};
+
+static struct wlan_mem_prealloc wlan_mem_array[PREALLOC_WLAN_SEC_NUM] = {
+ {NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER)},
+ {NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER)},
+ {NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER)},
+ {NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER)},
+ {NULL, (WLAN_SECTION_SIZE_7 + PREALLOC_WLAN_SECTION_HEADER)},
+ {NULL, (WLAN_SECTION_SIZE_8 + PREALLOC_WLAN_SECTION_HEADER)}
+};
+
+void *wlan_static_scan_buf0;
+void *wlan_static_scan_buf1;
+void *bcmdhd_mem_prealloc(int section, unsigned long size)
+{
+ if (section == WLAN_STATIC_PKT_BUF) {
+ pr_info("1 %s: section=%d, wlan_static_skb=%p\n",
+ __func__, section, wlan_static_skb);
+ return wlan_static_skb;
+ }
+ if (section == WLAN_STATIC_SCAN_BUF0) {
+ pr_info("2 %s: section=%d, wlan_static_scan_buf0=%p\n",
+ __func__, section, wlan_static_scan_buf0);
+ return wlan_static_scan_buf0;
+ }
+ if (section == WLAN_STATIC_SCAN_BUF1) {
+ pr_info("3 %s: section=%d, wlan_static_scan_buf1=%p\n",
+ __func__, section, wlan_static_scan_buf1);
+ return wlan_static_scan_buf1;
+ }
+ if (section == WLAN_STATIC_DHD_INFO) {
+ pr_info("4 %s: section=%d, wlan_mem_array[4]=%p\n",
+ __func__, section, wlan_mem_array[4].mem_ptr);
+ return wlan_mem_array[4].mem_ptr;
+ }
+ if (section == WLAN_STATIC_DHD_WLFC_INFO) {
+ pr_info("5 %s: section=%d, wlan_mem_array[5]=%p\n",
+ __func__, section, wlan_mem_array[5].mem_ptr);
+ return wlan_mem_array[5].mem_ptr;
+ }
+ if ((section < 0) || (section > PREALLOC_WLAN_SEC_NUM)) {
+ pr_info("5 %s: out of section %d\n", __func__, section);
+ return NULL;
+ }
+
+ if (wlan_mem_array[section].size < size) {
+ pr_info("6 %s: wlan_mem_array[section].size=%lu, size=%lu\n",
+ __func__, wlan_mem_array[section].size, size);
+ return NULL;
+ }
+ pr_info("7 %s: wlan_mem_array[section].mem_ptr=%p, size=%lu\n",
+ __func__, &wlan_mem_array[section], size);
+
+ return wlan_mem_array[section].mem_ptr;
+}
+EXPORT_SYMBOL(bcmdhd_mem_prealloc);
+
+int bcmdhd_init_wlan_mem(void)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < 8; i++) {
+ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_1PAGE_BUFSIZE);
+ if (!wlan_static_skb[i])
+ goto err_skb_alloc;
+ pr_debug("1 %s: wlan_static_skb[%d]=%p, size=%lu\n",
+ __func__, i, wlan_static_skb[i], DHD_SKB_1PAGE_BUFSIZE);
+ }
+
+ for (; i < 16; i++) {
+ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_2PAGE_BUFSIZE);
+ if (!wlan_static_skb[i])
+ goto err_skb_alloc;
+ pr_debug("2 %s: wlan_static_skb[%d]=%p, size=%lu\n",
+ __func__, i, wlan_static_skb[i], DHD_SKB_2PAGE_BUFSIZE);
+ }
+
+ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_4PAGE_BUFSIZE);
+ if (!wlan_static_skb[i])
+ goto err_skb_alloc;
+ pr_debug("3 %s: wlan_static_skb[%d]=%p, size=%lu\n",
+ __func__, i, wlan_static_skb[i], DHD_SKB_4PAGE_BUFSIZE);
+
+ for (i = 0; i < PREALLOC_WLAN_SEC_NUM; i++) {
+ wlan_mem_array[i].mem_ptr =
+ kmalloc(wlan_mem_array[i].size, GFP_KERNEL);
+
+ if (!wlan_mem_array[i].mem_ptr)
+ goto err_mem_alloc;
+ pr_debug("4 %s: wlan_mem_array[%d]=%p, size=%lu\n",
+ __func__, i, wlan_static_skb[i],
+ wlan_mem_array[i].size);
+ }
+
+ wlan_static_scan_buf0 = kmalloc(65536, GFP_KERNEL);
+ if (!wlan_static_scan_buf0)
+ goto err_mem_alloc;
+ pr_debug("5 %s: wlan_static_scan_buf0=%p, size=%d\n",
+ __func__, wlan_static_scan_buf0, 65536);
+
+ wlan_static_scan_buf1 = kmalloc(65536, GFP_KERNEL);
+ if (!wlan_static_scan_buf1)
+ goto err_mem_alloc;
+ pr_debug("6 %s: wlan_static_scan_buf1=%p, size=%d\n",
+ __func__, wlan_static_scan_buf1, 65536);
+
+ pr_info("%s: WIFI MEM Allocated\n", __func__);
+ return 0;
+
+err_mem_alloc:
+ pr_err("Failed to mem_alloc for WLAN\n");
+ for (j = 0; j < i; j++)
+ kfree(wlan_mem_array[j].mem_ptr);
+
+ i = WLAN_SKB_BUF_NUM;
+
+err_skb_alloc:
+ pr_err("Failed to skb_alloc for WLAN\n");
+ for (j = 0; j < i; j++)
+ dev_kfree_skb(wlan_static_skb[j]);
+
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(bcmdhd_init_wlan_mem);
+#endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */
+#if 0
+static int __init bcmdhd_wlan_init(void)
+{
+ pr_info("%s()\n", __func__);
+
+#ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM
+ bcmdhd_init_wlan_mem();
+#endif
+
+ return 0;
+}
+
+device_initcall(bcmdhd_wlan_init);
+#endif
+MODULE_DESCRIPTION("Amlogic S912/wifi driver");
+MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
+MODULE_LICENSE("GPL")
--- /dev/null
+/*
+ * drivers/amlogic/wifi/wifi_dt.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/amlogic/wifi_dt.h>
+#ifdef CONFIG_BCMDHD_USE_STATIC_BUF
+#include <linux/amlogic/dhd_buf.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/media/old_cpu_version.h>
+#include <linux/amlogic/iomap.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/pwm.h>
+#include <linux/amlogic/pwm_meson.h>
+#include "../../gpio/gpiolib.h"
+#define OWNER_NAME "sdio_wifi"
+
+int wifi_power_gpio;
+int wifi_power_gpio2;
+
+/*
+ *there are two pwm channel outputs using one gpio
+ *for gxtvbb and the follows soc
+ */
+struct pwm_config_gxtvbb {
+ unsigned int pwm_channel1;
+ unsigned int pwm_channel2;
+ unsigned int pwm_config1[3];
+ unsigned int pwm_config2[3];
+};
+
+struct pwm_config_gxbb {
+ unsigned int pwm_channel;
+ unsigned int pwm_config[2];
+};
+
+struct wifi_plat_info {
+ int interrupt_pin;
+ int irq_num;
+ int irq_trigger_type;
+
+ int power_on_pin;
+ int power_on_pin_level;
+ int power_on_pin_OD;
+ int power_on_pin2;
+
+ int clock_32k_pin;
+ struct gpio_desc *interrupt_desc;
+ struct gpio_desc *powe_desc;
+
+ int plat_info_valid;
+ struct pinctrl *p;
+ struct device *dev;
+ struct pwm_config_gxtvbb gxtv_conf;
+ struct pwm_config_gxbb gxb_conf;
+};
+
+#define WIFI_POWER_MODULE_NAME "wifi_power"
+#define WIFI_POWER_DRIVER_NAME "wifi_power"
+#define WIFI_POWER_DEVICE_NAME "wifi_power"
+#define WIFI_POWER_CLASS_NAME "wifi_power"
+
+#define USB_POWER_UP _IO('m', 1)
+#define USB_POWER_DOWN _IO('m', 2)
+#define SDIO_POWER_UP _IO('m', 3)
+#define SDIO_POWER_DOWN _IO('m', 4)
+#define SDIO_GET_DEV_TYPE _IO('m', 5)
+static struct wifi_plat_info wifi_info;
+static dev_t wifi_power_devno;
+static struct cdev *wifi_power_cdev;
+static struct device *devp;
+struct wifi_power_platform_data *pdata;
+
+static int usb_power;
+#define BT_BIT 0
+#define WIFI_BIT 1
+static DEFINE_MUTEX(wifi_bt_mutex);
+
+#define WIFI_INFO(fmt, args...) \
+ dev_info(wifi_info.dev, "[%s] " fmt, __func__, ##args)
+
+#ifdef CONFIG_OF
+static const struct of_device_id wifi_match[] = {
+ {
+ .compatible = "amlogic, aml_wifi",
+ .data = (void *)&wifi_info
+ },
+ {},
+};
+
+static struct wifi_plat_info *wifi_get_driver_data
+ (struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(wifi_match, pdev->dev.of_node);
+ return (struct wifi_plat_info *)match->data;
+}
+#else
+#define wifi_match NULL
+#endif
+
+
+
+#define SHOW_PIN_OWN(pin_str, pin_num) \
+ WIFI_INFO("%s(%d)\n", pin_str, pin_num)
+
+static int set_power(int value)
+{
+ if (!wifi_info.power_on_pin_OD) {
+ if (wifi_info.power_on_pin_level)
+ return gpio_direction_output(wifi_info.power_on_pin,
+ !value);
+ else
+ return gpio_direction_output(wifi_info.power_on_pin,
+ value);
+ } else {
+ if (wifi_info.power_on_pin_level) {
+ if (value)
+ gpio_direction_input(wifi_info.power_on_pin);
+ else
+ gpio_direction_output(wifi_info.power_on_pin,
+ 0);
+ } else {
+ if (value)
+ gpio_direction_output(wifi_info.power_on_pin,
+ 0);
+ else
+ gpio_direction_input(wifi_info.power_on_pin);
+ }
+ }
+ return 0;
+}
+
+static int set_power2(int value)
+{
+ if (wifi_info.power_on_pin_level)
+ return gpio_direction_output(wifi_info.power_on_pin2,
+ !value);
+ else
+ return gpio_direction_output(wifi_info.power_on_pin2,
+ value);
+}
+
+static int set_wifi_power(int is_power)
+{
+ int ret = 0;
+
+ if (is_power) {
+ if (wifi_info.power_on_pin) {
+ ret = set_power(1);
+ if (ret)
+ WIFI_INFO("power up failed(%d)\n", ret);
+ }
+ if (wifi_info.power_on_pin2) {
+ ret = set_power2(1);
+ if (ret)
+ WIFI_INFO("power2 up failed(%d)\n", ret);
+ }
+ } else {
+ if (wifi_info.power_on_pin) {
+ ret = set_power(0);
+ if (ret)
+ WIFI_INFO("power down failed(%d)\n", ret);
+ }
+ if (wifi_info.power_on_pin2) {
+ ret = set_power2(0);
+ if (ret)
+ WIFI_INFO("power2 down failed(%d)\n", ret);
+ }
+ }
+ return ret;
+}
+
+static void usb_power_control(int is_power, int shift)
+{
+ mutex_lock(&wifi_bt_mutex);
+ if (is_power) {
+ if (!usb_power)
+ set_wifi_power(is_power);
+ usb_power |= (1 << shift);
+ WIFI_INFO("Set %s power on !\n", (shift ? "WiFi":"BT"));
+ } else {
+ usb_power &= ~(1 << shift);
+ if (!usb_power)
+ set_wifi_power(is_power);
+ WIFI_INFO("Set %s power down !\n", (shift ? "WiFi":"BT"));
+ }
+ mutex_unlock(&wifi_bt_mutex);
+}
+
+void set_usb_bt_power(int is_power)
+{
+ usb_power_control(is_power, BT_BIT);
+}
+EXPORT_SYMBOL(set_usb_bt_power);
+
+void set_usb_wifi_power(int is_power)
+{
+ usb_power_control(is_power, WIFI_BIT);
+}
+
+static int wifi_power_open(struct inode *inode, struct file *file)
+{
+ struct cdev *cdevp = inode->i_cdev;
+
+ file->private_data = cdevp;
+ return 0;
+}
+
+static int wifi_power_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+static long wifi_power_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ char dev_type[10] = {'\0'};
+
+ switch (cmd) {
+ case USB_POWER_UP:
+ set_usb_wifi_power(0);
+ mdelay(200);
+ set_usb_wifi_power(1);
+ break;
+ case USB_POWER_DOWN:
+ set_usb_wifi_power(0);
+ WIFI_INFO(KERN_INFO "Set usb_sdio wifi power down!\n");
+ break;
+ case SDIO_POWER_UP:
+ extern_wifi_set_enable(0);
+ mdelay(200);
+ extern_wifi_set_enable(1);
+ mdelay(200);
+ sdio_reinit();
+ WIFI_INFO("Set sdio wifi power up!\n");
+ break;
+ case SDIO_POWER_DOWN:
+ extern_wifi_set_enable(0);
+ break;
+ case SDIO_GET_DEV_TYPE:
+ memcpy(dev_type, get_wifi_inf(), strlen(get_wifi_inf()));
+ WIFI_INFO("wifi interface dev type: %s, length = %d\n",
+ dev_type, (int)strlen(dev_type));
+ if (copy_to_user((char __user *)arg,
+ dev_type, strlen(dev_type)))
+ return -ENOTTY;
+ break;
+ default:
+ WIFI_INFO("usb wifi_power_ioctl: default !!!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct file_operations wifi_power_fops = {
+ .unlocked_ioctl = wifi_power_ioctl,
+ .compat_ioctl = wifi_power_ioctl,
+ .open = wifi_power_open,
+ .release = wifi_power_release,
+};
+
+static struct class wifi_power_class = {
+ .name = WIFI_POWER_CLASS_NAME,
+ .owner = THIS_MODULE,
+};
+
+static int wifi_setup_dt(void)
+{
+ int ret;
+
+ WIFI_INFO("wifi_setup_dt\n");
+ if (!wifi_info.plat_info_valid) {
+ WIFI_INFO("wifi_setup_dt : invalid device tree setting\n");
+ return -1;
+ }
+
+ /* setup irq */
+ if (wifi_info.interrupt_pin) {
+ ret = gpio_request(wifi_info.interrupt_pin,
+ OWNER_NAME);
+ if (ret)
+ WIFI_INFO("interrupt_pin request failed(%d)\n", ret);
+
+ ret = gpio_direction_input(wifi_info.interrupt_pin);
+ if (ret)
+ WIFI_INFO("set interrupt_pin input failed(%d)\n", ret);
+
+ wifi_info.irq_num = gpio_to_irq(wifi_info.interrupt_pin);
+ if (wifi_info.irq_num)
+ WIFI_INFO("irq num is:(%d)\n", wifi_info.irq_num);
+
+ SHOW_PIN_OWN("interrupt_pin", wifi_info.interrupt_pin);
+ }
+
+ /* setup power */
+ if (wifi_info.power_on_pin) {
+ ret = gpio_request(wifi_info.power_on_pin, OWNER_NAME);
+ if (ret)
+ WIFI_INFO("power_on_pin request failed(%d)\n", ret);
+ if (wifi_info.power_on_pin_level)
+ ret = set_power(1);
+ else
+ ret = set_power(0);
+ if (ret)
+ WIFI_INFO("power_on_pin output failed(%d)\n", ret);
+ SHOW_PIN_OWN("power_on_pin", wifi_info.power_on_pin);
+ }
+
+ if (wifi_info.power_on_pin2) {
+ ret = gpio_request(wifi_info.power_on_pin2,
+ OWNER_NAME);
+ if (ret)
+ WIFI_INFO("power_on_pin2 request failed(%d)\n", ret);
+ if (wifi_info.power_on_pin_level)
+ ret = set_power2(1);
+ else
+ ret = set_power2(0);
+ if (ret)
+ WIFI_INFO("power_on_pin2 output failed(%d)\n", ret);
+ SHOW_PIN_OWN("power_on_pin2", wifi_info.power_on_pin2);
+ }
+
+ return 0;
+}
+
+static void wifi_teardown_dt(void)
+{
+
+ WIFI_INFO("wifi_teardown_dt\n");
+ if (!wifi_info.plat_info_valid) {
+ WIFI_INFO("wifi_teardown_dt : invalid device tree setting\n");
+ return;
+ }
+
+ if (wifi_info.power_on_pin)
+ gpio_free(wifi_info.power_on_pin);
+
+ if (wifi_info.power_on_pin2)
+ gpio_free(wifi_info.power_on_pin2);
+
+ if (wifi_info.interrupt_pin)
+ gpio_free(wifi_info.interrupt_pin);
+
+}
+
+
+/*
+ * fot gxb soc
+ */
+int pwm_single_channel_conf_dt(struct wifi_plat_info *plat)
+{
+ phandle pwm_phandle;
+ int val;
+ int ret;
+ int count = 2;
+ struct device_node *np_wifi_pwm_conf = plat->dev->of_node;
+
+ ret = of_property_read_u32(np_wifi_pwm_conf, "pwm_config", &val);
+ if (ret) {
+ pr_err("not match wifi_pwm_config node\n");
+ return -1;
+
+ } else {
+ pwm_phandle = val;
+ np_wifi_pwm_conf = of_find_node_by_phandle(pwm_phandle);
+ if (!np_wifi_pwm_conf) {
+ pr_err("can't find wifi_pwm_config node\n");
+ return -1;
+ }
+ }
+
+ ret = of_property_read_u32(np_wifi_pwm_conf, "pwm_channel",
+ &(plat->gxb_conf.pwm_channel));
+ if (ret) {
+ pr_err("not config pwm channel num\n");
+ return -1;
+ }
+
+ ret = of_property_read_u32_array(np_wifi_pwm_conf, "pwm_channel_conf",
+ (plat->gxb_conf.pwm_config), count);
+ if (ret) {
+ pr_err("not config pwm channel parameters\n");
+ return -1;
+ }
+
+ WIFI_INFO("pwm phandle val=%x,pwm-channel=%d\n",
+ val, plat->gxb_conf.pwm_channel);
+ WIFI_INFO("pwm_config[0] = %d,pwm_config[1] = %d\n",
+ plat->gxb_conf.pwm_config[0], plat->gxb_conf.pwm_config[1]);
+ WIFI_INFO("wifi pwm dt ok\n");
+
+ return 0;
+}
+
+/*
+ *configuration for single pwm
+ */
+int pwm_single_channel_conf(struct wifi_plat_info *plat)
+{
+ struct pwm_device *pwm_ch = NULL;
+ struct aml_pwm_chip *aml_chip = NULL;
+ struct pwm_config_gxbb pg = plat->gxb_conf;
+ unsigned int pwm_num = pg.pwm_channel;
+ unsigned int pwm_period = pg.pwm_config[0];
+ unsigned int pwm_duty = pg.pwm_config[1];
+
+ pwm_ch = pwm_request(pwm_num, NULL);
+ if (IS_ERR(pwm_ch)) {
+ WIFI_INFO("request pwm %d failed\n",
+ plat->gxb_conf.pwm_channel);
+ }
+ aml_chip = to_aml_pwm_chip(pwm_ch->chip);
+ pwm_set_period(pwm_ch, pwm_period);
+ pwm_config(pwm_ch, pwm_duty, pwm_period);
+ pwm_enable(pwm_ch);
+ WIFI_INFO("wifi pwm conf ok\n");
+
+ return 0;
+}
+
+int pwm_double_channel_conf_dt(struct wifi_plat_info *plat)
+{
+ phandle pwm_phandle;
+ int val;
+ int ret;
+ int count = 3;
+ int i;
+ struct device_node *np_wifi_pwm_conf = plat->dev->of_node;
+
+ ret = of_property_read_u32(np_wifi_pwm_conf, "pwm_config", &val);
+ if (ret) {
+ pr_err("not match wifi_pwm_config node\n");
+ return -1;
+ }
+ pwm_phandle = val;
+ np_wifi_pwm_conf = of_find_node_by_phandle(pwm_phandle);
+ if (!np_wifi_pwm_conf) {
+ pr_err("can't find wifi_pwm_config node\n");
+ return -1;
+ }
+
+
+ ret = of_property_read_u32(np_wifi_pwm_conf, "pwm_channel1",
+ &(plat->gxtv_conf.pwm_channel1));
+ if (ret) {
+ pr_err("not config pwm channel 1 num\n");
+ return -1;
+ }
+ ret = of_property_read_u32(np_wifi_pwm_conf, "pwm_channel2",
+ &(plat->gxtv_conf.pwm_channel2));
+ if (ret) {
+ pr_err("not config pwm channel 2 num\n");
+ return -1;
+ }
+ ret = of_property_read_u32_array(np_wifi_pwm_conf, "pwm_channel1_conf",
+ (plat->gxtv_conf.pwm_config1), count);
+ if (ret) {
+ pr_err("not config pwm channel 1 parameters\n");
+ return -1;
+ }
+ ret = of_property_read_u32_array(np_wifi_pwm_conf, "pwm_channel2_conf",
+ (plat->gxtv_conf.pwm_config2), count);
+ if (ret) {
+ pr_err("not config pwm channel 2 parameters\n");
+ return -1;
+ }
+
+ WIFI_INFO("pwm phandle val=%x;pwm-channel1=%d;pwm-channel2=%d\n",
+ val, plat->gxtv_conf.pwm_channel1,
+ plat->gxtv_conf.pwm_channel2);
+ for (i = 0; i < count; i++) {
+ WIFI_INFO("pwm_config1[%d] = %d\n",
+ i, plat->gxtv_conf.pwm_config1[i]);
+ WIFI_INFO("pwm_config2[%d] = %d\n",
+ i, plat->gxtv_conf.pwm_config2[i]);
+ }
+ WIFI_INFO("wifi pwm dt ok\n");
+
+ return 0;
+}
+/*
+ *configuration for double pwm
+ */
+int pwm_double_channel_conf(struct wifi_plat_info *plat)
+{
+ struct pwm_device *pwm_ch1 = NULL;
+ struct pwm_device *pwm_ch2 = NULL;
+ struct aml_pwm_chip *aml_chip1 = NULL;
+ struct aml_pwm_chip *aml_chip2 = NULL;
+ struct pwm_config_gxtvbb pg = plat->gxtv_conf;
+ unsigned int pwm_ch1_num = pg.pwm_channel1;
+ unsigned int pwm_ch2_num = pg.pwm_channel2;
+ unsigned int pwm_ch1_period = pg.pwm_config1[0];
+ unsigned int pwm_ch1_duty = pg.pwm_config1[1];
+ unsigned int pwm_ch1_times = pg.pwm_config1[2];
+ unsigned int pwm_ch2_period = pg.pwm_config2[0];
+ unsigned int pwm_ch2_duty = pg.pwm_config2[1];
+ unsigned int pwm_ch2_times = pg.pwm_config2[2];
+
+
+ pwm_ch1 = pwm_request(pwm_ch1_num, NULL);
+ if (IS_ERR(pwm_ch1)) {
+ WIFI_INFO("request pwm %d failed\n",
+ plat->gxtv_conf.pwm_channel1);
+ }
+ pwm_ch2 = pwm_request(pwm_ch2_num, NULL);
+ if (IS_ERR(pwm_ch2)) {
+ WIFI_INFO("request pwm %d failed\n",
+ plat->gxtv_conf.pwm_channel2);
+ }
+
+ aml_chip1 = to_aml_pwm_chip(pwm_ch1->chip);
+ aml_chip2 = to_aml_pwm_chip(pwm_ch2->chip);
+
+ pwm_set_period(pwm_ch1, pwm_ch1_period);
+ pwm_set_period(pwm_ch2, pwm_ch2_period);
+
+ pwm_config(pwm_ch1, pwm_ch1_duty, pwm_ch1_period);
+ pwm_config(pwm_ch2, pwm_ch2_duty, pwm_ch2_period);
+
+ pwm_set_times(aml_chip1, pwm_ch1_num, pwm_ch1_times);
+ pwm_set_times(aml_chip2, pwm_ch2_num, pwm_ch2_times);
+
+ pwm_enable(pwm_ch1);
+ pwm_enable(pwm_ch2);
+ WIFI_INFO("wifi pwm conf ok\n");
+
+ return 0;
+}
+
+static int wifi_dev_probe(struct platform_device *pdev)
+{
+ int ret;
+
+#ifdef CONFIG_OF
+ struct wifi_plat_info *plat;
+ const char *value;
+ struct gpio_desc *desc;
+#else
+ struct wifi_plat_info *plat =
+ (struct wifi_plat_info *)(pdev->dev.platform_data);
+#endif
+
+#ifdef CONFIG_OF
+ if (pdev->dev.of_node) {
+ plat = wifi_get_driver_data(pdev);
+ plat->plat_info_valid = 0;
+ plat->dev = &pdev->dev;
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "interrupt_pin", &value);
+ if (ret) {
+ WIFI_INFO("no interrupt pin");
+ plat->interrupt_pin = 0;
+ } else {
+ desc = of_get_named_gpiod_flags(pdev->dev.of_node,
+ "interrupt_pin", 0, NULL);
+ plat->interrupt_desc = desc;
+ plat->interrupt_pin = desc_to_gpio(desc);
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "irq_trigger_type", &value);
+ if (ret) {
+ WIFI_INFO("no irq_trigger_type");
+ plat->irq_trigger_type = 0;
+ return -1;
+ }
+
+ if (strcmp(value, "GPIO_IRQ_HIGH") == 0)
+ plat->irq_trigger_type = GPIO_IRQ_HIGH;
+ else if (strcmp(value, "GPIO_IRQ_LOW") == 0)
+ plat->irq_trigger_type = GPIO_IRQ_LOW;
+ else if (strcmp(value, "GPIO_IRQ_RISING") == 0)
+ plat->irq_trigger_type = GPIO_IRQ_RISING;
+ else if (strcmp(value, "GPIO_IRQ_FALLING") == 0)
+ plat->irq_trigger_type = GPIO_IRQ_FALLING;
+ else {
+ WIFI_INFO("unknown irq trigger type-%s\n",
+ value);
+ return -1;
+ }
+ }
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "power_on_pin", &value);
+ if (ret) {
+ WIFI_INFO("no power_on_pin");
+ plat->power_on_pin = 0;
+ plat->power_on_pin_OD = 0;
+ } else {
+ wifi_power_gpio = 1;
+ desc = of_get_named_gpiod_flags(pdev->dev.of_node,
+ "power_on_pin", 0, NULL);
+ plat->powe_desc = desc;
+ plat->power_on_pin = desc_to_gpio(desc);
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "power_on_pin_level", &plat->power_on_pin_level);
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "power_on_pin_OD", &plat->power_on_pin_OD);
+ if (ret)
+ plat->power_on_pin_OD = 0;
+ pr_info("wifi: power_on_pin_OD = %d;\n", plat->power_on_pin_OD);
+ ret = of_property_read_string(pdev->dev.of_node,
+ "power_on_pin2", &value);
+ if (ret) {
+ WIFI_INFO("no power_on_pin2");
+ plat->power_on_pin2 = 0;
+ } else {
+ wifi_power_gpio2 = 1;
+ desc = of_get_named_gpiod_flags(pdev->dev.of_node,
+ "power_on_pin2", 0, NULL);
+ plat->power_on_pin2 = desc_to_gpio(desc);
+ }
+
+ if (of_get_property(pdev->dev.of_node,
+ "pinctrl-names", NULL)) {
+ unsigned int pwm_misc;
+ unsigned int pwm_time_count;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) {
+ WIFI_INFO("set pwm as 32k output");
+ aml_write_cbus(0x21b0, 0x16d016e);
+ aml_write_cbus(0x21b5, 0x16d016d);
+
+ pwm_time_count = aml_read_cbus(0x21b4);
+ pwm_time_count &= ~(0xffff << 16);
+ pwm_time_count |= ((3 << 16) | (2 << 24));
+ aml_write_cbus(0x21b4, pwm_time_count);
+
+ pwm_misc = aml_read_cbus(0x21b2);
+ pwm_misc &= ~((0x7f << 8) | (3 << 4) |
+ (1 << 2) | (1 << 0));
+ pwm_misc |= ((3 << 24) | (1 << 15) |
+ (0 << 8) | (0 << 4));
+ aml_write_cbus(0x21b2, (pwm_misc | (1 << 0)));
+
+ } else if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) {
+
+ WIFI_INFO("set pwm as 32k output");
+ aml_write_cbus(0x21b0, 0x7f107f2);
+ pwm_misc = aml_read_cbus(0x21b2);
+ pwm_misc &= ~((0x7f << 8) | (3 << 4) |
+ (1 << 2) | (1 << 0));
+ pwm_misc |= ((1 << 15) | (4 << 8) | (3 << 4));
+ aml_write_cbus(0x21b2, pwm_misc);
+ aml_write_cbus(0x21b2, (pwm_misc | (1 << 0)));
+
+ } else if (get_cpu_type() == MESON_CPU_MAJOR_ID_M8B) {
+
+ WIFI_INFO("set pwm as 32k output");
+ aml_write_cbus(0x21b0, 0x7980799);
+ pwm_misc = aml_read_cbus(0x21b2);
+ pwm_misc &= ~((0x7f << 8) | (3 << 4) |
+ (1 << 2) | (1 << 0));
+ pwm_misc |= ((1 << 15) | (4 << 8) | (2 << 4));
+ aml_write_cbus(0x21b2, pwm_misc);
+ aml_write_cbus(0x21b2, (pwm_misc | (1 << 0)));
+ }
+
+ plat->p = devm_pinctrl_get_select(&pdev->dev,
+ "wifi_32k_pins");
+ }
+#ifdef CONFIG_BCMDHD_USE_STATIC_BUF
+ if (of_get_property(pdev->dev.of_node,
+ "dhd_static_buf", NULL)) {
+ WIFI_INFO("dhd_static_buf setup\n");
+ bcmdhd_init_wlan_mem();
+ }
+#endif
+
+ plat->plat_info_valid = 1;
+
+ WIFI_INFO("interrupt_pin=%d\n", plat->interrupt_pin);
+ WIFI_INFO("irq_num=%d, irq_trigger_type=%d\n",
+ plat->irq_num, plat->irq_trigger_type);
+ WIFI_INFO("power_on_pin=%d\n", plat->power_on_pin);
+ WIFI_INFO("clock_32k_pin=%d\n", plat->clock_32k_pin);
+ }
+#endif
+ ret = alloc_chrdev_region(&wifi_power_devno,
+ 0, 1, WIFI_POWER_DRIVER_NAME);
+ if (ret < 0) {
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = class_register(&wifi_power_class);
+ if (ret < 0)
+ goto error1;
+ wifi_power_cdev = cdev_alloc();
+ if (!wifi_power_cdev)
+ goto error2;
+ cdev_init(wifi_power_cdev, &wifi_power_fops);
+ wifi_power_cdev->owner = THIS_MODULE;
+ ret = cdev_add(wifi_power_cdev, wifi_power_devno, 1);
+ if (ret)
+ goto error3;
+ devp = device_create(&wifi_power_class, NULL,
+ wifi_power_devno, NULL, WIFI_POWER_DEVICE_NAME);
+ if (IS_ERR(devp)) {
+ ret = PTR_ERR(devp);
+ goto error3;
+ }
+ devp->platform_data = pdata;
+
+ wifi_setup_dt();
+
+ return 0;
+error3:
+ cdev_del(wifi_power_cdev);
+error2:
+ class_unregister(&wifi_power_class);
+error1:
+ unregister_chrdev_region(wifi_power_devno, 1);
+out:
+ return ret;
+}
+
+static int wifi_dev_remove(struct platform_device *pdev)
+{
+ WIFI_INFO("wifi_dev_remove\n");
+ wifi_teardown_dt();
+ return 0;
+}
+
+static struct platform_driver wifi_plat_driver = {
+ .probe = wifi_dev_probe,
+ .remove = wifi_dev_remove,
+ .driver = {
+ .name = "aml_wifi",
+ .owner = THIS_MODULE,
+ .of_match_table = wifi_match
+ },
+};
+
+static int __init wifi_dt_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&wifi_plat_driver);
+ return ret;
+}
+/* module_init(wifi_dt_init); */
+fs_initcall_sync(wifi_dt_init);
+
+static void __exit wifi_dt_exit(void)
+{
+ platform_driver_unregister(&wifi_plat_driver);
+}
+module_exit(wifi_dt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("wifi device tree driver");
+
+/**************** wifi mac *****************/
+u8 WIFI_MAC[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static unsigned char chartonum(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A') + 10;
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a') + 10;
+ return 0;
+}
+
+static int __init mac_addr_set(char *line)
+{
+ unsigned char mac[6];
+ int i = 0;
+
+ WIFI_INFO("try to wifi mac from emmc key!\n");
+ for (i = 0; i < 6 && line[0] != '\0' && line[1] != '\0'; i++) {
+ mac[i] = chartonum(line[0]) << 4 | chartonum(line[1]);
+ line += 3;
+ }
+ memcpy(WIFI_MAC, mac, 6);
+ WIFI_INFO("uboot setup mac-addr: %x:%x:%x:%x:%x:%x\n",
+ WIFI_MAC[0], WIFI_MAC[1], WIFI_MAC[2], WIFI_MAC[3], WIFI_MAC[4],
+ WIFI_MAC[5]);
+
+ return 1;
+}
+
+__setup("mac_wifi=", mac_addr_set);
+
+u8 *wifi_get_mac(void)
+{
+ return WIFI_MAC;
+}
+EXPORT_SYMBOL(wifi_get_mac);
+
+void extern_wifi_set_enable(int is_on)
+{
+
+ if (is_on) {
+ set_wifi_power(1);
+ WIFI_INFO("WIFI Enable! %d\n", wifi_info.power_on_pin);
+ } else {
+ set_wifi_power(0);
+ WIFI_INFO("WIFI Disable! %d\n", wifi_info.power_on_pin);
+
+ }
+}
+EXPORT_SYMBOL(extern_wifi_set_enable);
+
+int wifi_irq_num(void)
+{
+ return wifi_info.irq_num;
+}
+EXPORT_SYMBOL(wifi_irq_num);
+
+int wifi_irq_trigger_level(void)
+{
+ return wifi_info.irq_trigger_type;
+}
+EXPORT_SYMBOL(wifi_irq_trigger_level);
+MODULE_DESCRIPTION("Amlogic S912/wifi driver");
+MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * include/linux/amlogic/bt_device.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PLAT_MESON_BT_DEVICE_H
+#define __PLAT_MESON_BT_DEVICE_H
+
+struct bt_dev_data {
+ int gpio_reset;
+ int gpio_en;
+ int power_low_level;
+ int power_on_pin_OD;
+};
+
+#endif
--- /dev/null
+/*
+ * include/linux/amlogic/wifi_dt.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _wifi_dt_h_
+#define _wifi_dt_h_
+
+extern void sdio_reinit(void);
+extern char *get_wifi_inf(void);
+void extern_wifi_set_enable(int is_on);
+int wifi_irq_num(void);
+
+#endif /* _wifi_dt_h_ */
--- /dev/null
+/*
+ * include/linux/amlogic/wifi_power.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PLAT_MESON_WIFI_POWER_H
+#define __PLAT_MESON_WIFI_POWER_H
+
+struct wifi_power_platform_data {
+ int power_gpio;
+ int power_gpio2;
+ int (*set_power)(int val);
+ int (*set_reset)(int val);
+ int (*set_carddetect)(int val);
+ void * (*mem_prealloc)(int section, unsigned long size);
+ int (*get_mac_addr)(unsigned char *buf);
+ void * (*get_country_code)(char *ccode);
+ void (*usb_set_power)(int val);
+};
+
+int wifi_power_control(int power_up);
+extern void sdio_reinit(void);
+
+#endif /*__PLAT_MESON_WIFI_POWER_H*/