1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2021~2022 NXP 4 * 5 * The driver exports a standard gpiochip interface 6 * to control the PIN resources on SCU domain. 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/gpio/driver.h> 12 #include <linux/platform_device.h> 13 #include <linux/firmware/imx/svc/rm.h> 14 #include <dt-bindings/firmware/imx/rsrc.h> 15 16 struct scu_gpio_priv { 17 struct gpio_chip chip; 18 struct mutex lock; 19 struct device *dev; 20 struct imx_sc_ipc *handle; 21 }; 22 23 static unsigned int scu_rsrc_arr[] = { 24 IMX_SC_R_BOARD_R0, 25 IMX_SC_R_BOARD_R1, 26 IMX_SC_R_BOARD_R2, 27 IMX_SC_R_BOARD_R3, 28 IMX_SC_R_BOARD_R4, 29 IMX_SC_R_BOARD_R5, 30 IMX_SC_R_BOARD_R6, 31 IMX_SC_R_BOARD_R7, 32 }; 33 34 static int imx_scu_gpio_get(struct gpio_chip *chip, unsigned int offset) 35 { 36 struct scu_gpio_priv *priv = gpiochip_get_data(chip); 37 int level; 38 int err; 39 40 if (offset >= chip->ngpio) 41 return -EINVAL; 42 43 mutex_lock(&priv->lock); 44 45 /* to read PIN state via scu api */ 46 err = imx_sc_misc_get_control(priv->handle, 47 scu_rsrc_arr[offset], 0, &level); 48 mutex_unlock(&priv->lock); 49 50 if (err) { 51 dev_err(priv->dev, "SCU get failed: %d\n", err); 52 return err; 53 } 54 55 return level; 56 } 57 58 static void imx_scu_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) 59 { 60 struct scu_gpio_priv *priv = gpiochip_get_data(chip); 61 int err; 62 63 if (offset >= chip->ngpio) 64 return; 65 66 mutex_lock(&priv->lock); 67 68 /* to set PIN output level via scu api */ 69 err = imx_sc_misc_set_control(priv->handle, 70 scu_rsrc_arr[offset], 0, value); 71 mutex_unlock(&priv->lock); 72 73 if (err) 74 dev_err(priv->dev, "SCU set (%d) failed: %d\n", 75 scu_rsrc_arr[offset], err); 76 } 77 78 static int imx_scu_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) 79 { 80 if (offset >= chip->ngpio) 81 return -EINVAL; 82 83 return GPIO_LINE_DIRECTION_OUT; 84 } 85 86 static int imx_scu_gpio_probe(struct platform_device *pdev) 87 { 88 struct device *dev = &pdev->dev; 89 struct scu_gpio_priv *priv; 90 struct gpio_chip *gc; 91 int ret; 92 93 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 94 if (!priv) 95 return -ENOMEM; 96 97 ret = imx_scu_get_handle(&priv->handle); 98 if (ret) 99 return ret; 100 101 priv->dev = dev; 102 mutex_init(&priv->lock); 103 104 gc = &priv->chip; 105 gc->base = -1; 106 gc->parent = dev; 107 gc->ngpio = ARRAY_SIZE(scu_rsrc_arr); 108 gc->label = dev_name(dev); 109 gc->get = imx_scu_gpio_get; 110 gc->set = imx_scu_gpio_set; 111 gc->get_direction = imx_scu_gpio_get_direction; 112 113 platform_set_drvdata(pdev, priv); 114 115 return devm_gpiochip_add_data(dev, gc, priv); 116 } 117 118 static const struct of_device_id imx_scu_gpio_dt_ids[] = { 119 { .compatible = "fsl,imx8qxp-sc-gpio" }, 120 { /* sentinel */ } 121 }; 122 123 static struct platform_driver imx_scu_gpio_driver = { 124 .driver = { 125 .name = "gpio-imx-scu", 126 .of_match_table = imx_scu_gpio_dt_ids, 127 }, 128 .probe = imx_scu_gpio_probe, 129 }; 130 131 static int __init _imx_scu_gpio_init(void) 132 { 133 return platform_driver_register(&imx_scu_gpio_driver); 134 } 135 136 subsys_initcall_sync(_imx_scu_gpio_init); 137 138 MODULE_AUTHOR("Shenwei Wang <shenwei.wang@nxp.com>"); 139 MODULE_DESCRIPTION("NXP GPIO over IMX SCU API"); 140