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