Skip to content

Commit 18eeef4

Browse files
diandersJiri Kosina
authored andcommitted
HID: i2c-hid: goodix: Tie the reset line to true state of the regulator
The regulator for the touchscreen could be: * A dedicated regulator just for the touchscreen. * A regulator shared with something else in the system. * An always-on regulator. How we want the "reset" line to behave depends a bit on which of those three cases we're in. Currently the code is written with the assumption that it has a dedicated regulator, but that's not really guaranteed to be the case. The problem we run into is that if we leave the touchscreen powered on (because someone else is requesting the regulator or it's an always-on regulator) and we assert reset then we apparently burn an extra 67 mW of power. That's not great. Let's instead tie the control of the reset line to the true state of the regulator as reported by regulator notifiers. If we have an always-on regulator our notifier will never be called. If we have a shared regulator then our notifier will be called when the touchscreen is truly turned on or truly turned off. Using notifiers like this nicely handles all the cases without resorting to hacks like pretending that there is no "reset" GPIO if we have an always-on regulator. NOTE: if the regulator is on a shared line it's still possible that things could be a little off. Specifically, this case is not handled even after this patch: 1. Suspend goodix (send "sleep", goodix stops requesting regulator on) 2. Other regulator user turns off (regulator fully turns off). 3. Goodix driver gets notified and asserts reset. 4. Other regulator user turns on. 5. Goodix driver gets notified and deasserts reset. 6. Nobody resumes goodix. With that set of steps we'll have reset deasserted but we will have lost the results of the I2C_HID_PWR_SLEEP from the suspend path. That means we might be in higher power than we could be even if the goodix driver thinks things are suspended. Presumably, however, we're still in better shape than if we were asserting "reset" the whole time. If somehow the above situation is actually affecting someone and we want to do better we can deal with it when we have a real use case. Signed-off-by: Douglas Anderson <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent df04fbe commit 18eeef4

File tree

1 file changed

+79
-13
lines changed

1 file changed

+79
-13
lines changed

drivers/hid/i2c-hid/i2c-hid-of-goodix.c

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,49 +26,84 @@ struct i2c_hid_of_goodix {
2626
struct i2chid_ops ops;
2727

2828
struct regulator *vdd;
29+
struct notifier_block nb;
30+
struct mutex regulator_mutex;
2931
struct gpio_desc *reset_gpio;
3032
const struct goodix_i2c_hid_timing_data *timings;
3133
};
3234

33-
static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
35+
static void goodix_i2c_hid_deassert_reset(struct i2c_hid_of_goodix *ihid_goodix,
36+
bool regulator_just_turned_on)
3437
{
35-
struct i2c_hid_of_goodix *ihid_goodix =
36-
container_of(ops, struct i2c_hid_of_goodix, ops);
37-
int ret;
38-
39-
ret = regulator_enable(ihid_goodix->vdd);
40-
if (ret)
41-
return ret;
42-
43-
if (ihid_goodix->timings->post_power_delay_ms)
38+
if (regulator_just_turned_on && ihid_goodix->timings->post_power_delay_ms)
4439
msleep(ihid_goodix->timings->post_power_delay_ms);
4540

4641
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
4742
if (ihid_goodix->timings->post_gpio_reset_delay_ms)
4843
msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
44+
}
4945

50-
return 0;
46+
static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
47+
{
48+
struct i2c_hid_of_goodix *ihid_goodix =
49+
container_of(ops, struct i2c_hid_of_goodix, ops);
50+
51+
return regulator_enable(ihid_goodix->vdd);
5152
}
5253

5354
static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
5455
{
5556
struct i2c_hid_of_goodix *ihid_goodix =
5657
container_of(ops, struct i2c_hid_of_goodix, ops);
5758

58-
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
5959
regulator_disable(ihid_goodix->vdd);
6060
}
6161

62+
static int ihid_goodix_vdd_notify(struct notifier_block *nb,
63+
unsigned long event,
64+
void *ignored)
65+
{
66+
struct i2c_hid_of_goodix *ihid_goodix =
67+
container_of(nb, struct i2c_hid_of_goodix, nb);
68+
int ret = NOTIFY_OK;
69+
70+
mutex_lock(&ihid_goodix->regulator_mutex);
71+
72+
switch (event) {
73+
case REGULATOR_EVENT_PRE_DISABLE:
74+
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
75+
break;
76+
77+
case REGULATOR_EVENT_ENABLE:
78+
goodix_i2c_hid_deassert_reset(ihid_goodix, true);
79+
break;
80+
81+
case REGULATOR_EVENT_ABORT_DISABLE:
82+
goodix_i2c_hid_deassert_reset(ihid_goodix, false);
83+
break;
84+
85+
default:
86+
ret = NOTIFY_DONE;
87+
break;
88+
}
89+
90+
mutex_unlock(&ihid_goodix->regulator_mutex);
91+
92+
return ret;
93+
}
94+
6295
static int i2c_hid_of_goodix_probe(struct i2c_client *client,
6396
const struct i2c_device_id *id)
6497
{
6598
struct i2c_hid_of_goodix *ihid_goodix;
66-
99+
int ret;
67100
ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
68101
GFP_KERNEL);
69102
if (!ihid_goodix)
70103
return -ENOMEM;
71104

105+
mutex_init(&ihid_goodix->regulator_mutex);
106+
72107
ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
73108
ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
74109

@@ -84,6 +119,37 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client,
84119

85120
ihid_goodix->timings = device_get_match_data(&client->dev);
86121

122+
/*
123+
* We need to control the "reset" line in lockstep with the regulator
124+
* actually turning on an off instead of just when we make the request.
125+
* This matters if the regulator is shared with another consumer.
126+
* - If the regulator is off then we must assert reset. The reset
127+
* line is active low and on some boards it could cause a current
128+
* leak if left high.
129+
* - If the regulator is on then we don't want reset asserted for very
130+
* long. Holding the controller in reset apparently draws extra
131+
* power.
132+
*/
133+
mutex_lock(&ihid_goodix->regulator_mutex);
134+
ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify;
135+
ret = regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb);
136+
if (ret) {
137+
mutex_unlock(&ihid_goodix->regulator_mutex);
138+
return dev_err_probe(&client->dev, ret,
139+
"regulator notifier request failed\n");
140+
}
141+
142+
/*
143+
* If someone else is holding the regulator on (or the regulator is
144+
* an always-on one) we might never be told to deassert reset. Do it
145+
* now. Here we'll assume that someone else might have _just
146+
* barely_ turned the regulator on so we'll do the full
147+
* "post_power_delay" just in case.
148+
*/
149+
if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd))
150+
goodix_i2c_hid_deassert_reset(ihid_goodix, true);
151+
mutex_unlock(&ihid_goodix->regulator_mutex);
152+
87153
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001);
88154
}
89155

0 commit comments

Comments
 (0)