1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * w1-gpio - GPIO w1 bus master driver |
4 | * |
5 | * Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi> |
6 | */ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/gpio/consumer.h> |
13 | #include <linux/of_platform.h> |
14 | #include <linux/err.h> |
15 | #include <linux/of.h> |
16 | #include <linux/delay.h> |
17 | |
18 | #include <linux/w1.h> |
19 | |
20 | struct w1_gpio_ddata { |
21 | struct gpio_desc *gpiod; |
22 | struct gpio_desc *pullup_gpiod; |
23 | unsigned int pullup_duration; |
24 | }; |
25 | |
26 | static u8 w1_gpio_set_pullup(void *data, int delay) |
27 | { |
28 | struct w1_gpio_ddata *ddata = data; |
29 | |
30 | if (delay) { |
31 | ddata->pullup_duration = delay; |
32 | } else { |
33 | if (ddata->pullup_duration) { |
34 | /* |
35 | * This will OVERRIDE open drain emulation and force-pull |
36 | * the line high for some time. |
37 | */ |
38 | gpiod_set_raw_value(desc: ddata->gpiod, value: 1); |
39 | msleep(msecs: ddata->pullup_duration); |
40 | /* |
41 | * This will simply set the line as input since we are doing |
42 | * open drain emulation in the GPIO library. |
43 | */ |
44 | gpiod_set_value(desc: ddata->gpiod, value: 1); |
45 | } |
46 | ddata->pullup_duration = 0; |
47 | } |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static void w1_gpio_write_bit(void *data, u8 bit) |
53 | { |
54 | struct w1_gpio_ddata *ddata = data; |
55 | |
56 | gpiod_set_value(desc: ddata->gpiod, value: bit); |
57 | } |
58 | |
59 | static u8 w1_gpio_read_bit(void *data) |
60 | { |
61 | struct w1_gpio_ddata *ddata = data; |
62 | |
63 | return gpiod_get_value(desc: ddata->gpiod) ? 1 : 0; |
64 | } |
65 | |
66 | #if defined(CONFIG_OF) |
67 | static const struct of_device_id w1_gpio_dt_ids[] = { |
68 | { .compatible = "w1-gpio" }, |
69 | {} |
70 | }; |
71 | MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids); |
72 | #endif |
73 | |
74 | static int w1_gpio_probe(struct platform_device *pdev) |
75 | { |
76 | struct w1_bus_master *master; |
77 | struct w1_gpio_ddata *ddata; |
78 | struct device *dev = &pdev->dev; |
79 | struct device_node *np = dev->of_node; |
80 | /* Enforce open drain mode by default */ |
81 | enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN; |
82 | int err; |
83 | |
84 | ddata = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ddata), GFP_KERNEL); |
85 | if (!ddata) |
86 | return -ENOMEM; |
87 | |
88 | /* |
89 | * This parameter means that something else than the gpiolib has |
90 | * already set the line into open drain mode, so we should just |
91 | * driver it high/low like we are in full control of the line and |
92 | * open drain will happen transparently. |
93 | */ |
94 | if (of_property_present(np, propname: "linux,open-drain" )) |
95 | gflags = GPIOD_OUT_LOW; |
96 | |
97 | master = devm_kzalloc(dev, size: sizeof(struct w1_bus_master), |
98 | GFP_KERNEL); |
99 | if (!master) |
100 | return -ENOMEM; |
101 | |
102 | ddata->gpiod = devm_gpiod_get_index(dev, NULL, idx: 0, flags: gflags); |
103 | if (IS_ERR(ptr: ddata->gpiod)) { |
104 | dev_err(dev, "gpio_request (pin) failed\n" ); |
105 | return PTR_ERR(ptr: ddata->gpiod); |
106 | } |
107 | |
108 | ddata->pullup_gpiod = |
109 | devm_gpiod_get_index_optional(dev, NULL, index: 1, flags: GPIOD_OUT_LOW); |
110 | if (IS_ERR(ptr: ddata->pullup_gpiod)) { |
111 | dev_err(dev, "gpio_request_one " |
112 | "(ext_pullup_enable_pin) failed\n" ); |
113 | return PTR_ERR(ptr: ddata->pullup_gpiod); |
114 | } |
115 | |
116 | master->data = ddata; |
117 | master->read_bit = w1_gpio_read_bit; |
118 | gpiod_direction_output(desc: ddata->gpiod, value: 1); |
119 | master->write_bit = w1_gpio_write_bit; |
120 | |
121 | /* |
122 | * If we are using open drain emulation from the GPIO library, |
123 | * we need to use this pullup function that hammers the line |
124 | * high using a raw accessor to provide pull-up for the w1 |
125 | * line. |
126 | */ |
127 | if (gflags == GPIOD_OUT_LOW_OPEN_DRAIN) |
128 | master->set_pullup = w1_gpio_set_pullup; |
129 | |
130 | err = w1_add_master_device(master); |
131 | if (err) { |
132 | dev_err(dev, "w1_add_master device failed\n" ); |
133 | return err; |
134 | } |
135 | |
136 | if (ddata->pullup_gpiod) |
137 | gpiod_set_value(desc: ddata->pullup_gpiod, value: 1); |
138 | |
139 | platform_set_drvdata(pdev, data: master); |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | static void w1_gpio_remove(struct platform_device *pdev) |
145 | { |
146 | struct w1_bus_master *master = platform_get_drvdata(pdev); |
147 | struct w1_gpio_ddata *ddata = master->data; |
148 | |
149 | if (ddata->pullup_gpiod) |
150 | gpiod_set_value(desc: ddata->pullup_gpiod, value: 0); |
151 | |
152 | w1_remove_master_device(master); |
153 | } |
154 | |
155 | static struct platform_driver w1_gpio_driver = { |
156 | .driver = { |
157 | .name = "w1-gpio" , |
158 | .of_match_table = of_match_ptr(w1_gpio_dt_ids), |
159 | }, |
160 | .probe = w1_gpio_probe, |
161 | .remove_new = w1_gpio_remove, |
162 | }; |
163 | |
164 | module_platform_driver(w1_gpio_driver); |
165 | |
166 | MODULE_DESCRIPTION("GPIO w1 bus master driver" ); |
167 | MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>" ); |
168 | MODULE_LICENSE("GPL" ); |
169 | |