1*4342bf63SThomas Richard // SPDX-License-Identifier: GPL-2.0-or-later 2*4342bf63SThomas Richard /* 3*4342bf63SThomas Richard * Congatec Board Controller GPIO driver 4*4342bf63SThomas Richard * 5*4342bf63SThomas Richard * Copyright (C) 2024 Bootlin 6*4342bf63SThomas Richard * Author: Thomas Richard <thomas.richard@bootlin.com> 7*4342bf63SThomas Richard */ 8*4342bf63SThomas Richard 9*4342bf63SThomas Richard #include <linux/gpio/driver.h> 10*4342bf63SThomas Richard #include <linux/mfd/cgbc.h> 11*4342bf63SThomas Richard #include <linux/module.h> 12*4342bf63SThomas Richard #include <linux/mutex.h> 13*4342bf63SThomas Richard #include <linux/platform_device.h> 14*4342bf63SThomas Richard 15*4342bf63SThomas Richard #define CGBC_GPIO_NGPIO 14 16*4342bf63SThomas Richard 17*4342bf63SThomas Richard #define CGBC_GPIO_CMD_GET 0x64 18*4342bf63SThomas Richard #define CGBC_GPIO_CMD_SET 0x65 19*4342bf63SThomas Richard #define CGBC_GPIO_CMD_DIR_GET 0x66 20*4342bf63SThomas Richard #define CGBC_GPIO_CMD_DIR_SET 0x67 21*4342bf63SThomas Richard 22*4342bf63SThomas Richard struct cgbc_gpio_data { 23*4342bf63SThomas Richard struct gpio_chip chip; 24*4342bf63SThomas Richard struct cgbc_device_data *cgbc; 25*4342bf63SThomas Richard struct mutex lock; 26*4342bf63SThomas Richard }; 27*4342bf63SThomas Richard 28*4342bf63SThomas Richard static int cgbc_gpio_cmd(struct cgbc_device_data *cgbc, 29*4342bf63SThomas Richard u8 cmd0, u8 cmd1, u8 cmd2, u8 *value) 30*4342bf63SThomas Richard { 31*4342bf63SThomas Richard u8 cmd[3] = {cmd0, cmd1, cmd2}; 32*4342bf63SThomas Richard 33*4342bf63SThomas Richard return cgbc_command(cgbc, cmd, sizeof(cmd), value, 1, NULL); 34*4342bf63SThomas Richard } 35*4342bf63SThomas Richard 36*4342bf63SThomas Richard static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset) 37*4342bf63SThomas Richard { 38*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 39*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc; 40*4342bf63SThomas Richard int ret; 41*4342bf63SThomas Richard u8 val; 42*4342bf63SThomas Richard 43*4342bf63SThomas Richard scoped_guard(mutex, &gpio->lock) 44*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); 45*4342bf63SThomas Richard 46*4342bf63SThomas Richard offset %= 8; 47*4342bf63SThomas Richard 48*4342bf63SThomas Richard if (ret) 49*4342bf63SThomas Richard return ret; 50*4342bf63SThomas Richard else 51*4342bf63SThomas Richard return (int)(val & (u8)BIT(offset)); 52*4342bf63SThomas Richard } 53*4342bf63SThomas Richard 54*4342bf63SThomas Richard static void __cgbc_gpio_set(struct gpio_chip *chip, 55*4342bf63SThomas Richard unsigned int offset, int value) 56*4342bf63SThomas Richard { 57*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 58*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc; 59*4342bf63SThomas Richard u8 val; 60*4342bf63SThomas Richard int ret; 61*4342bf63SThomas Richard 62*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); 63*4342bf63SThomas Richard if (ret) 64*4342bf63SThomas Richard return; 65*4342bf63SThomas Richard 66*4342bf63SThomas Richard if (value) 67*4342bf63SThomas Richard val |= BIT(offset % 8); 68*4342bf63SThomas Richard else 69*4342bf63SThomas Richard val &= ~(BIT(offset % 8)); 70*4342bf63SThomas Richard 71*4342bf63SThomas Richard cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val); 72*4342bf63SThomas Richard } 73*4342bf63SThomas Richard 74*4342bf63SThomas Richard static void cgbc_gpio_set(struct gpio_chip *chip, 75*4342bf63SThomas Richard unsigned int offset, int value) 76*4342bf63SThomas Richard { 77*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 78*4342bf63SThomas Richard 79*4342bf63SThomas Richard scoped_guard(mutex, &gpio->lock) 80*4342bf63SThomas Richard __cgbc_gpio_set(chip, offset, value); 81*4342bf63SThomas Richard } 82*4342bf63SThomas Richard 83*4342bf63SThomas Richard static int cgbc_gpio_direction_set(struct gpio_chip *chip, 84*4342bf63SThomas Richard unsigned int offset, int direction) 85*4342bf63SThomas Richard { 86*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 87*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc; 88*4342bf63SThomas Richard int ret; 89*4342bf63SThomas Richard u8 val; 90*4342bf63SThomas Richard 91*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); 92*4342bf63SThomas Richard if (ret) 93*4342bf63SThomas Richard goto end; 94*4342bf63SThomas Richard 95*4342bf63SThomas Richard if (direction == GPIO_LINE_DIRECTION_IN) 96*4342bf63SThomas Richard val &= ~(BIT(offset % 8)); 97*4342bf63SThomas Richard else 98*4342bf63SThomas Richard val |= BIT(offset % 8); 99*4342bf63SThomas Richard 100*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_SET, (offset > 7) ? 1 : 0, val, &val); 101*4342bf63SThomas Richard 102*4342bf63SThomas Richard end: 103*4342bf63SThomas Richard return ret; 104*4342bf63SThomas Richard } 105*4342bf63SThomas Richard 106*4342bf63SThomas Richard static int cgbc_gpio_direction_input(struct gpio_chip *chip, 107*4342bf63SThomas Richard unsigned int offset) 108*4342bf63SThomas Richard { 109*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 110*4342bf63SThomas Richard 111*4342bf63SThomas Richard guard(mutex)(&gpio->lock); 112*4342bf63SThomas Richard return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_IN); 113*4342bf63SThomas Richard } 114*4342bf63SThomas Richard 115*4342bf63SThomas Richard static int cgbc_gpio_direction_output(struct gpio_chip *chip, 116*4342bf63SThomas Richard unsigned int offset, int value) 117*4342bf63SThomas Richard { 118*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 119*4342bf63SThomas Richard 120*4342bf63SThomas Richard guard(mutex)(&gpio->lock); 121*4342bf63SThomas Richard 122*4342bf63SThomas Richard __cgbc_gpio_set(chip, offset, value); 123*4342bf63SThomas Richard return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT); 124*4342bf63SThomas Richard } 125*4342bf63SThomas Richard 126*4342bf63SThomas Richard static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) 127*4342bf63SThomas Richard { 128*4342bf63SThomas Richard struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 129*4342bf63SThomas Richard struct cgbc_device_data *cgbc = gpio->cgbc; 130*4342bf63SThomas Richard int ret; 131*4342bf63SThomas Richard u8 val; 132*4342bf63SThomas Richard 133*4342bf63SThomas Richard scoped_guard(mutex, &gpio->lock) 134*4342bf63SThomas Richard ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); 135*4342bf63SThomas Richard 136*4342bf63SThomas Richard if (ret) 137*4342bf63SThomas Richard return ret; 138*4342bf63SThomas Richard 139*4342bf63SThomas Richard if (val & BIT(offset % 8)) 140*4342bf63SThomas Richard return GPIO_LINE_DIRECTION_OUT; 141*4342bf63SThomas Richard else 142*4342bf63SThomas Richard return GPIO_LINE_DIRECTION_IN; 143*4342bf63SThomas Richard } 144*4342bf63SThomas Richard 145*4342bf63SThomas Richard static int cgbc_gpio_probe(struct platform_device *pdev) 146*4342bf63SThomas Richard { 147*4342bf63SThomas Richard struct device *dev = &pdev->dev; 148*4342bf63SThomas Richard struct cgbc_device_data *cgbc = dev_get_drvdata(dev->parent); 149*4342bf63SThomas Richard struct cgbc_gpio_data *gpio; 150*4342bf63SThomas Richard struct gpio_chip *chip; 151*4342bf63SThomas Richard int ret; 152*4342bf63SThomas Richard 153*4342bf63SThomas Richard gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); 154*4342bf63SThomas Richard if (!gpio) 155*4342bf63SThomas Richard return -ENOMEM; 156*4342bf63SThomas Richard 157*4342bf63SThomas Richard gpio->cgbc = cgbc; 158*4342bf63SThomas Richard 159*4342bf63SThomas Richard platform_set_drvdata(pdev, gpio); 160*4342bf63SThomas Richard 161*4342bf63SThomas Richard chip = &gpio->chip; 162*4342bf63SThomas Richard chip->label = dev_name(&pdev->dev); 163*4342bf63SThomas Richard chip->owner = THIS_MODULE; 164*4342bf63SThomas Richard chip->parent = dev; 165*4342bf63SThomas Richard chip->base = -1; 166*4342bf63SThomas Richard chip->direction_input = cgbc_gpio_direction_input; 167*4342bf63SThomas Richard chip->direction_output = cgbc_gpio_direction_output; 168*4342bf63SThomas Richard chip->get_direction = cgbc_gpio_get_direction; 169*4342bf63SThomas Richard chip->get = cgbc_gpio_get; 170*4342bf63SThomas Richard chip->set = cgbc_gpio_set; 171*4342bf63SThomas Richard chip->ngpio = CGBC_GPIO_NGPIO; 172*4342bf63SThomas Richard 173*4342bf63SThomas Richard ret = devm_mutex_init(dev, &gpio->lock); 174*4342bf63SThomas Richard if (ret) 175*4342bf63SThomas Richard return ret; 176*4342bf63SThomas Richard 177*4342bf63SThomas Richard ret = devm_gpiochip_add_data(dev, chip, gpio); 178*4342bf63SThomas Richard if (ret) 179*4342bf63SThomas Richard return dev_err_probe(dev, ret, "Could not register GPIO chip\n"); 180*4342bf63SThomas Richard 181*4342bf63SThomas Richard return 0; 182*4342bf63SThomas Richard } 183*4342bf63SThomas Richard 184*4342bf63SThomas Richard static struct platform_driver cgbc_gpio_driver = { 185*4342bf63SThomas Richard .driver = { 186*4342bf63SThomas Richard .name = "cgbc-gpio", 187*4342bf63SThomas Richard }, 188*4342bf63SThomas Richard .probe = cgbc_gpio_probe, 189*4342bf63SThomas Richard }; 190*4342bf63SThomas Richard 191*4342bf63SThomas Richard module_platform_driver(cgbc_gpio_driver); 192*4342bf63SThomas Richard 193*4342bf63SThomas Richard MODULE_DESCRIPTION("Congatec Board Controller GPIO Driver"); 194*4342bf63SThomas Richard MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); 195*4342bf63SThomas Richard MODULE_LICENSE("GPL"); 196*4342bf63SThomas Richard MODULE_ALIAS("platform:cgbc-gpio"); 197