| /* SPDX-License-Identifier: GPL-2.0-only |
| * Copyright 2020 Google LLC. All Rights Reserved. |
| * |
| * platform driver to interface with uwb reset pin on aoc |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/device.h> |
| #include <linux/gpio/driver.h> |
| #include <linux/platform_device.h> |
| |
| #include "aoc.h" |
| #include "aoc-interface.h" |
| #include "aoc_uwb_service_dev.h" |
| |
| #define AOC_UWB_PDRV_NAME "aoc_uwb_pdrv" |
| |
| static int value_get(struct gpio_chip *gc, unsigned offset) |
| { |
| struct CMD_UWB_GET_RESET_GPIO cmd = { 0 }; |
| int ret = -1; |
| |
| if (offset) { |
| dev_err(gc->parent, "aoc_uwb_pdrv wrong offset\n"); |
| goto err_exit; |
| } |
| |
| AocCmdHdrSet(&cmd.parent, CMD_UWB_GET_RESET_GPIO_ID, sizeof(cmd)); |
| ret = aoc_uwb_service_send(&cmd, sizeof(cmd)); |
| if (ret < 0) { |
| dev_err(gc->parent, "error sending pin get to aoc\n"); |
| goto err_exit; |
| } |
| |
| return cmd.gpio_value; |
| |
| err_exit: |
| return ret; |
| } |
| |
| static void value_set(struct gpio_chip *gc, unsigned offset, int value) |
| { |
| struct CMD_UWB_SET_RESET_GPIO cmd = { 0 }; |
| int ret; |
| |
| dev_dbg(gc->parent, "value=%d\n", value); |
| if (offset) { |
| dev_err(gc->parent, "wrong offset\n"); |
| return; |
| } |
| |
| AocCmdHdrSet(&cmd.parent, CMD_UWB_SET_RESET_GPIO_ID, sizeof(cmd)); |
| cmd.gpio_value = value; |
| ret = aoc_uwb_service_send(&cmd, sizeof(cmd)); |
| if (ret < 0) |
| dev_err(gc->parent, "error sending pin set to aoc ret=%d\n", ret); |
| } |
| |
| static int aoc_uwb_reset_set_direction(struct gpio_chip *gc, bool output) |
| { |
| struct CMD_UWB_SET_RESET_GPIO_DIRECTION cmd = { 0 }; |
| int ret = 0; |
| |
| dev_dbg(gc->parent, "dir=%s\n", output ? "output" : "input"); |
| AocCmdHdrSet(&cmd.parent, CMD_UWB_SET_RESET_GPIO_DIRECTION_ID, sizeof(cmd)); |
| cmd.gpio_direction = output ? 1 : 0; |
| ret = aoc_uwb_service_send(&cmd, sizeof(cmd)); |
| if (ret < 0) |
| dev_err(gc->parent, "error sending pin direction to aoc\n"); |
| return ret < 0 ? ret : 0; |
| } |
| |
| static int direction_input(struct gpio_chip *gc, unsigned offset) |
| { |
| return aoc_uwb_reset_set_direction(gc, 0); |
| } |
| |
| static int direction_output(struct gpio_chip *gc, unsigned offset, int value) |
| { |
| value_set(gc, offset, value); |
| return aoc_uwb_reset_set_direction(gc, 1); |
| } |
| |
| static struct gpio_chip chip = { |
| .set = value_set, |
| .get = value_get, |
| .direction_input = direction_input, |
| .direction_output = direction_output, |
| .request = gpiochip_generic_request, |
| .free = gpiochip_generic_free, |
| .set_config = gpiochip_generic_config, |
| .base = -1, |
| }; |
| |
| static int uwb_pdrv_pin_init(struct platform_device *pdev) |
| { |
| struct device_node *node; |
| struct device *dev = &pdev->dev; |
| int ngpio = 0; |
| int ret = 0; |
| |
| node = pdev->dev.of_node; |
| ret = of_property_read_u32(node, "ngpio", &ngpio); |
| if (ret != 0) { |
| dev_err(dev, "fail to read ngpio\n"); |
| return ret; |
| } |
| |
| chip.label = node->name; |
| chip.parent = dev; |
| chip.of_node = node; |
| chip.ngpio = ngpio; |
| return devm_gpiochip_add_data(dev, &chip, NULL); |
| } |
| |
| static int aoc_uwb_pdrv_probe(struct platform_device *pdev) |
| { |
| if (!aoc_uwb_service_ready()) |
| return -EPROBE_DEFER; |
| |
| return uwb_pdrv_pin_init(pdev); |
| } |
| |
| static int aoc_uwb_pdrv_remove(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| |
| static const struct of_device_id aoc_match[] = { |
| { |
| .compatible = "google,aoc_uwb_rst", |
| }, |
| {}, |
| }; |
| |
| static struct platform_driver aoc_uwb_pdrv = { |
| .driver = { |
| .name = AOC_UWB_PDRV_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = of_match_ptr(aoc_match) |
| }, |
| .probe = aoc_uwb_pdrv_probe, |
| .remove = aoc_uwb_pdrv_remove, |
| }; |
| |
| module_platform_driver(aoc_uwb_pdrv); |
| |
| MODULE_LICENSE("GPL v2"); |