162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * w1-gpio - GPIO w1 bus master driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/w1-gpio.h> 1362306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/w1.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic u8 w1_gpio_set_pullup(void *data, int delay) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct w1_gpio_platform_data *pdata = data; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci if (delay) { 2662306a36Sopenharmony_ci pdata->pullup_duration = delay; 2762306a36Sopenharmony_ci } else { 2862306a36Sopenharmony_ci if (pdata->pullup_duration) { 2962306a36Sopenharmony_ci /* 3062306a36Sopenharmony_ci * This will OVERRIDE open drain emulation and force-pull 3162306a36Sopenharmony_ci * the line high for some time. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci gpiod_set_raw_value(pdata->gpiod, 1); 3462306a36Sopenharmony_ci msleep(pdata->pullup_duration); 3562306a36Sopenharmony_ci /* 3662306a36Sopenharmony_ci * This will simply set the line as input since we are doing 3762306a36Sopenharmony_ci * open drain emulation in the GPIO library. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci gpiod_set_value(pdata->gpiod, 1); 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci pdata->pullup_duration = 0; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void w1_gpio_write_bit(void *data, u8 bit) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct w1_gpio_platform_data *pdata = data; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci gpiod_set_value(pdata->gpiod, bit); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic u8 w1_gpio_read_bit(void *data) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct w1_gpio_platform_data *pdata = data; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return gpiod_get_value(pdata->gpiod) ? 1 : 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#if defined(CONFIG_OF) 6262306a36Sopenharmony_cistatic const struct of_device_id w1_gpio_dt_ids[] = { 6362306a36Sopenharmony_ci { .compatible = "w1-gpio" }, 6462306a36Sopenharmony_ci {} 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, w1_gpio_dt_ids); 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int w1_gpio_probe(struct platform_device *pdev) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct w1_bus_master *master; 7262306a36Sopenharmony_ci struct w1_gpio_platform_data *pdata; 7362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 7462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 7562306a36Sopenharmony_ci /* Enforce open drain mode by default */ 7662306a36Sopenharmony_ci enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN; 7762306a36Sopenharmony_ci int err; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (of_have_populated_dt()) { 8062306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 8162306a36Sopenharmony_ci if (!pdata) 8262306a36Sopenharmony_ci return -ENOMEM; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * This parameter means that something else than the gpiolib has 8662306a36Sopenharmony_ci * already set the line into open drain mode, so we should just 8762306a36Sopenharmony_ci * driver it high/low like we are in full control of the line and 8862306a36Sopenharmony_ci * open drain will happen transparently. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci if (of_property_present(np, "linux,open-drain")) 9162306a36Sopenharmony_ci gflags = GPIOD_OUT_LOW; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci pdev->dev.platform_data = pdata; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci pdata = dev_get_platdata(dev); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!pdata) { 9862306a36Sopenharmony_ci dev_err(dev, "No configuration data\n"); 9962306a36Sopenharmony_ci return -ENXIO; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci master = devm_kzalloc(dev, sizeof(struct w1_bus_master), 10362306a36Sopenharmony_ci GFP_KERNEL); 10462306a36Sopenharmony_ci if (!master) 10562306a36Sopenharmony_ci return -ENOMEM; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci pdata->gpiod = devm_gpiod_get_index(dev, NULL, 0, gflags); 10862306a36Sopenharmony_ci if (IS_ERR(pdata->gpiod)) { 10962306a36Sopenharmony_ci dev_err(dev, "gpio_request (pin) failed\n"); 11062306a36Sopenharmony_ci return PTR_ERR(pdata->gpiod); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci pdata->pullup_gpiod = 11462306a36Sopenharmony_ci devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_LOW); 11562306a36Sopenharmony_ci if (IS_ERR(pdata->pullup_gpiod)) { 11662306a36Sopenharmony_ci dev_err(dev, "gpio_request_one " 11762306a36Sopenharmony_ci "(ext_pullup_enable_pin) failed\n"); 11862306a36Sopenharmony_ci return PTR_ERR(pdata->pullup_gpiod); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci master->data = pdata; 12262306a36Sopenharmony_ci master->read_bit = w1_gpio_read_bit; 12362306a36Sopenharmony_ci gpiod_direction_output(pdata->gpiod, 1); 12462306a36Sopenharmony_ci master->write_bit = w1_gpio_write_bit; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * If we are using open drain emulation from the GPIO library, 12862306a36Sopenharmony_ci * we need to use this pullup function that hammers the line 12962306a36Sopenharmony_ci * high using a raw accessor to provide pull-up for the w1 13062306a36Sopenharmony_ci * line. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci if (gflags == GPIOD_OUT_LOW_OPEN_DRAIN) 13362306a36Sopenharmony_ci master->set_pullup = w1_gpio_set_pullup; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci err = w1_add_master_device(master); 13662306a36Sopenharmony_ci if (err) { 13762306a36Sopenharmony_ci dev_err(dev, "w1_add_master device failed\n"); 13862306a36Sopenharmony_ci return err; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (pdata->enable_external_pullup) 14262306a36Sopenharmony_ci pdata->enable_external_pullup(1); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (pdata->pullup_gpiod) 14562306a36Sopenharmony_ci gpiod_set_value(pdata->pullup_gpiod, 1); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci platform_set_drvdata(pdev, master); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int w1_gpio_remove(struct platform_device *pdev) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct w1_bus_master *master = platform_get_drvdata(pdev); 15562306a36Sopenharmony_ci struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (pdata->enable_external_pullup) 15862306a36Sopenharmony_ci pdata->enable_external_pullup(0); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (pdata->pullup_gpiod) 16162306a36Sopenharmony_ci gpiod_set_value(pdata->pullup_gpiod, 0); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci w1_remove_master_device(master); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int __maybe_unused w1_gpio_suspend(struct device *dev) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct w1_gpio_platform_data *pdata = dev_get_platdata(dev); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (pdata->enable_external_pullup) 17362306a36Sopenharmony_ci pdata->enable_external_pullup(0); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int __maybe_unused w1_gpio_resume(struct device *dev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct w1_gpio_platform_data *pdata = dev_get_platdata(dev); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (pdata->enable_external_pullup) 18362306a36Sopenharmony_ci pdata->enable_external_pullup(1); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(w1_gpio_pm_ops, w1_gpio_suspend, w1_gpio_resume); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic struct platform_driver w1_gpio_driver = { 19162306a36Sopenharmony_ci .driver = { 19262306a36Sopenharmony_ci .name = "w1-gpio", 19362306a36Sopenharmony_ci .pm = &w1_gpio_pm_ops, 19462306a36Sopenharmony_ci .of_match_table = of_match_ptr(w1_gpio_dt_ids), 19562306a36Sopenharmony_ci }, 19662306a36Sopenharmony_ci .probe = w1_gpio_probe, 19762306a36Sopenharmony_ci .remove = w1_gpio_remove, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cimodule_platform_driver(w1_gpio_driver); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO w1 bus master driver"); 20362306a36Sopenharmony_ciMODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); 20462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 205