1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries 4 */ 5 6 #include <linux/fwnode.h> 7 #include <linux/gpio/consumer.h> 8 #include <linux/gpio/driver.h> 9 #include <linux/gpio/machine.h> 10 #include <linux/gpio/property.h> 11 #include <linux/notifier.h> 12 #include <linux/platform_device.h> 13 #include <linux/property.h> 14 15 #include <kunit/platform_device.h> 16 #include <kunit/test.h> 17 18 #define GPIO_TEST_PROVIDER "gpio-test-provider" 19 #define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer" 20 #define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer" 21 22 static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset) 23 { 24 return GPIO_LINE_DIRECTION_OUT; 25 } 26 27 static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value) 28 { 29 return 0; 30 } 31 32 static int gpio_test_provider_probe(struct platform_device *pdev) 33 { 34 struct device *dev = &pdev->dev; 35 struct gpio_chip *gc; 36 37 gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); 38 if (!gc) 39 return -ENOMEM; 40 41 gc->base = -1; 42 gc->ngpio = 4; 43 gc->label = "gpio-swnode-consumer-test-device"; 44 gc->parent = dev; 45 gc->owner = THIS_MODULE; 46 47 gc->get_direction = gpio_test_provider_get_direction; 48 gc->set = gpio_test_provider_set; 49 50 return devm_gpiochip_add_data(dev, gc, NULL); 51 } 52 53 static struct platform_driver gpio_test_provider_driver = { 54 .probe = gpio_test_provider_probe, 55 .driver = { 56 .name = GPIO_TEST_PROVIDER, 57 }, 58 }; 59 60 static const struct software_node gpio_test_provider_swnode = { 61 .name = "gpio-test-provider-primary", 62 }; 63 64 struct gpio_swnode_consumer_pdata { 65 bool gpio_ok; 66 }; 67 68 static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = { 69 .gpio_ok = false, 70 }; 71 72 static int gpio_swnode_consumer_probe(struct platform_device *pdev) 73 { 74 struct device *dev = &pdev->dev; 75 struct gpio_swnode_consumer_pdata *pdata = dev_get_platdata(dev); 76 struct gpio_desc *desc; 77 78 desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); 79 if (IS_ERR(desc)) 80 return PTR_ERR(desc); 81 82 pdata->gpio_ok = true; 83 84 return 0; 85 } 86 87 static struct platform_driver gpio_swnode_consumer_driver = { 88 .probe = gpio_swnode_consumer_probe, 89 .driver = { 90 .name = GPIO_SWNODE_TEST_CONSUMER, 91 }, 92 }; 93 94 static void gpio_swnode_lookup_by_primary(struct kunit *test) 95 { 96 struct gpio_swnode_consumer_pdata *pdata; 97 struct platform_device_info pdevinfo; 98 struct property_entry properties[2]; 99 struct platform_device *pdev; 100 bool bound = false; 101 int ret; 102 103 ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); 104 KUNIT_ASSERT_EQ(test, ret, 0); 105 106 ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); 107 KUNIT_ASSERT_EQ(test, ret, 0); 108 109 pdevinfo = (struct platform_device_info){ 110 .name = GPIO_TEST_PROVIDER, 111 .id = PLATFORM_DEVID_NONE, 112 .swnode = &gpio_test_provider_swnode, 113 }; 114 115 pdev = kunit_platform_device_register_full(test, &pdevinfo); 116 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); 117 118 properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", 119 &gpio_test_provider_swnode, 120 0, GPIO_ACTIVE_HIGH); 121 properties[1] = (struct property_entry){ }; 122 123 pdevinfo = (struct platform_device_info){ 124 .name = GPIO_SWNODE_TEST_CONSUMER, 125 .id = PLATFORM_DEVID_NONE, 126 .data = &gpio_swnode_pdata_template, 127 .size_data = sizeof(gpio_swnode_pdata_template), 128 .properties = properties, 129 }; 130 131 pdev = kunit_platform_device_register_full(test, &pdevinfo); 132 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); 133 134 wait_for_device_probe(); 135 scoped_guard(device, &pdev->dev) 136 bound = device_is_bound(&pdev->dev); 137 138 KUNIT_ASSERT_TRUE(test, bound); 139 140 pdata = dev_get_platdata(&pdev->dev); 141 KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); 142 } 143 144 static void gpio_swnode_lookup_by_secondary(struct kunit *test) 145 { 146 struct gpio_swnode_consumer_pdata *pdata; 147 struct platform_device_info pdevinfo; 148 struct property_entry properties[2]; 149 struct fwnode_handle *primary; 150 struct platform_device *pdev; 151 bool bound = false; 152 int ret; 153 154 /* 155 * Can't live on the stack as it will still get referenced in cleanup 156 * path after this function returns. 157 */ 158 primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL); 159 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); 160 161 ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); 162 KUNIT_ASSERT_EQ(test, ret, 0); 163 164 ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); 165 KUNIT_ASSERT_EQ(test, ret, 0); 166 167 fwnode_init(primary, NULL); 168 169 pdevinfo = (struct platform_device_info){ 170 .name = GPIO_TEST_PROVIDER, 171 .id = PLATFORM_DEVID_NONE, 172 .fwnode = primary, 173 .swnode = &gpio_test_provider_swnode, 174 }; 175 176 pdev = kunit_platform_device_register_full(test, &pdevinfo); 177 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); 178 179 properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", 180 &gpio_test_provider_swnode, 181 0, GPIO_ACTIVE_HIGH); 182 properties[1] = (struct property_entry){ }; 183 184 pdevinfo = (struct platform_device_info){ 185 .name = GPIO_SWNODE_TEST_CONSUMER, 186 .id = PLATFORM_DEVID_NONE, 187 .data = &gpio_swnode_pdata_template, 188 .size_data = sizeof(gpio_swnode_pdata_template), 189 .properties = properties, 190 }; 191 192 pdev = kunit_platform_device_register_full(test, &pdevinfo); 193 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); 194 195 wait_for_device_probe(); 196 scoped_guard(device, &pdev->dev) 197 bound = device_is_bound(&pdev->dev); 198 199 KUNIT_ASSERT_TRUE(test, bound); 200 201 pdata = dev_get_platdata(&pdev->dev); 202 KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); 203 } 204 205 static struct kunit_case gpio_swnode_lookup_tests[] = { 206 KUNIT_CASE(gpio_swnode_lookup_by_primary), 207 KUNIT_CASE(gpio_swnode_lookup_by_secondary), 208 { } 209 }; 210 211 static struct kunit_suite gpio_swnode_lookup_test_suite = { 212 .name = "gpio-swnode-lookup", 213 .test_cases = gpio_swnode_lookup_tests, 214 }; 215 216 static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier); 217 218 struct gpio_unbind_consumer_drvdata { 219 struct device *dev; 220 struct gpio_desc *desc; 221 struct notifier_block nb; 222 int set_retval; 223 }; 224 225 static int gpio_unbind_notify(struct notifier_block *nb, unsigned long action, 226 void *data) 227 { 228 struct gpio_unbind_consumer_drvdata *drvdata = 229 container_of(nb, struct gpio_unbind_consumer_drvdata, nb); 230 struct device *dev = data; 231 232 if (dev != drvdata->dev) 233 return NOTIFY_DONE; 234 235 drvdata->set_retval = gpiod_set_value_cansleep(drvdata->desc, 0); 236 237 return NOTIFY_OK; 238 } 239 240 static void gpio_unbind_unregister_notifier(void *data) 241 { 242 struct notifier_block *nb = data; 243 244 blocking_notifier_chain_unregister(&gpio_unbind_notifier, nb); 245 } 246 247 static int gpio_unbind_consumer_probe(struct platform_device *pdev) 248 { 249 struct gpio_unbind_consumer_drvdata *data; 250 struct device *dev = &pdev->dev; 251 int ret; 252 253 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 254 if (!data) 255 return -ENOMEM; 256 257 data->dev = dev; 258 259 data->desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); 260 if (IS_ERR(data->desc)) 261 return PTR_ERR(data->desc); 262 263 data->nb.notifier_call = gpio_unbind_notify; 264 ret = blocking_notifier_chain_register(&gpio_unbind_notifier, &data->nb); 265 if (ret) 266 return ret; 267 268 ret = devm_add_action_or_reset(dev, gpio_unbind_unregister_notifier, &data->nb); 269 if (ret) 270 return ret; 271 272 platform_set_drvdata(pdev, data); 273 274 return 0; 275 } 276 277 static struct platform_driver gpio_unbind_consumer_driver = { 278 .probe = gpio_unbind_consumer_probe, 279 .driver = { 280 .name = GPIO_UNBIND_TEST_CONSUMER, 281 }, 282 }; 283 284 static void gpio_unbind_with_consumers(struct kunit *test) 285 { 286 struct gpio_unbind_consumer_drvdata *cons_data; 287 struct platform_device_info pdevinfo; 288 struct property_entry properties[2]; 289 struct platform_device *prvd, *cons; 290 bool bound = false; 291 int ret; 292 293 ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); 294 KUNIT_ASSERT_EQ(test, ret, 0); 295 296 ret = kunit_platform_driver_register(test, &gpio_unbind_consumer_driver); 297 KUNIT_ASSERT_EQ(test, ret, 0); 298 299 pdevinfo = (struct platform_device_info){ 300 .name = GPIO_TEST_PROVIDER, 301 .id = PLATFORM_DEVID_NONE, 302 .swnode = &gpio_test_provider_swnode, 303 }; 304 305 prvd = kunit_platform_device_register_full(test, &pdevinfo); 306 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); 307 308 properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", 309 &gpio_test_provider_swnode, 310 0, GPIO_ACTIVE_HIGH); 311 properties[1] = (struct property_entry){ }; 312 313 pdevinfo = (struct platform_device_info){ 314 .name = GPIO_UNBIND_TEST_CONSUMER, 315 .id = PLATFORM_DEVID_NONE, 316 .properties = properties, 317 }; 318 319 cons = kunit_platform_device_register_full(test, &pdevinfo); 320 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); 321 322 wait_for_device_probe(); 323 scoped_guard(device, &cons->dev) 324 bound = device_is_bound(&cons->dev); 325 326 KUNIT_ASSERT_TRUE(test, bound); 327 328 kunit_platform_device_unregister(test, prvd); 329 330 ret = blocking_notifier_call_chain(&gpio_unbind_notifier, 0, &cons->dev); 331 KUNIT_ASSERT_EQ(test, ret, NOTIFY_OK); 332 333 scoped_guard(device, &cons->dev) { 334 cons_data = platform_get_drvdata(cons); 335 ret = cons_data->set_retval; 336 } 337 338 KUNIT_ASSERT_EQ(test, ret, -ENODEV); 339 } 340 341 static struct kunit_case gpio_unbind_with_consumers_tests[] = { 342 KUNIT_CASE(gpio_unbind_with_consumers), 343 { } 344 }; 345 346 static struct kunit_suite gpio_unbind_with_consumers_test_suite = { 347 .name = "gpio-unbind-with-consumers", 348 .test_cases = gpio_unbind_with_consumers_tests, 349 }; 350 351 kunit_test_suites( 352 &gpio_swnode_lookup_test_suite, 353 &gpio_unbind_with_consumers_test_suite, 354 ); 355 356 MODULE_DESCRIPTION("Test module for the GPIO subsystem"); 357 MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>"); 358 MODULE_LICENSE("GPL"); 359