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