^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Bluetooth built-in chip control
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) 2008 Dmitry Baryshkov
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/gpio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/rfkill.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include "tosa_bt.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) static void tosa_bt_on(struct tosa_bt_data *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) gpio_set_value(data->gpio_reset, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) gpio_set_value(data->gpio_pwr, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) gpio_set_value(data->gpio_reset, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) mdelay(20);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) gpio_set_value(data->gpio_reset, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static void tosa_bt_off(struct tosa_bt_data *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) gpio_set_value(data->gpio_reset, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) mdelay(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) gpio_set_value(data->gpio_pwr, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) gpio_set_value(data->gpio_reset, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static int tosa_bt_set_block(void *data, bool blocked)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) if (!blocked) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) pr_info("TOSA_BT: going ON\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) tosa_bt_on(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) pr_info("TOSA_BT: going OFF\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) tosa_bt_off(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) static const struct rfkill_ops tosa_bt_rfkill_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) .set_block = tosa_bt_set_block,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) static int tosa_bt_probe(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) struct rfkill *rfk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) struct tosa_bt_data *data = dev->dev.platform_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) rc = gpio_request(data->gpio_reset, "Bluetooth reset");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) if (rc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) goto err_reset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) rc = gpio_direction_output(data->gpio_reset, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) if (rc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) goto err_reset_dir;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) rc = gpio_request(data->gpio_pwr, "Bluetooth power");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) if (rc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) goto err_pwr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) rc = gpio_direction_output(data->gpio_pwr, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) if (rc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) goto err_pwr_dir;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) rfk = rfkill_alloc("tosa-bt", &dev->dev, RFKILL_TYPE_BLUETOOTH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) &tosa_bt_rfkill_ops, data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) if (!rfk) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) rc = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) goto err_rfk_alloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) rc = rfkill_register(rfk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) if (rc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) goto err_rfkill;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) platform_set_drvdata(dev, rfk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) err_rfkill:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) rfkill_destroy(rfk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) err_rfk_alloc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) tosa_bt_off(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) err_pwr_dir:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) gpio_free(data->gpio_pwr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) err_pwr:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) err_reset_dir:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) gpio_free(data->gpio_reset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) err_reset:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static int tosa_bt_remove(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct tosa_bt_data *data = dev->dev.platform_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct rfkill *rfk = platform_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) platform_set_drvdata(dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) if (rfk) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) rfkill_unregister(rfk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) rfkill_destroy(rfk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) rfk = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) tosa_bt_off(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) gpio_free(data->gpio_pwr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) gpio_free(data->gpio_reset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static struct platform_driver tosa_bt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) .probe = tosa_bt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .remove = tosa_bt_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) .name = "tosa-bt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) module_platform_driver(tosa_bt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) MODULE_AUTHOR("Dmitry Baryshkov");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) MODULE_DESCRIPTION("Bluetooth built-in chip control");