xref: /OK3568_Linux_fs/kernel/drivers/acpi/dock.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  dock.c - ACPI dock station driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2006, 2014, Intel Corp.
6*4882a593Smuzhiyun  *  Author: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
7*4882a593Smuzhiyun  *          Rafael J. Wysocki <rafael.j.wysocki@intel.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/moduleparam.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <linux/types.h>
15*4882a593Smuzhiyun #include <linux/notifier.h>
16*4882a593Smuzhiyun #include <linux/platform_device.h>
17*4882a593Smuzhiyun #include <linux/jiffies.h>
18*4882a593Smuzhiyun #include <linux/stddef.h>
19*4882a593Smuzhiyun #include <linux/acpi.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "internal.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun static bool immediate_undock = 1;
24*4882a593Smuzhiyun module_param(immediate_undock, bool, 0644);
25*4882a593Smuzhiyun MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
26*4882a593Smuzhiyun 	"undock immediately when the undock button is pressed, 0 will cause"
27*4882a593Smuzhiyun 	" the driver to wait for userspace to write the undock sysfs file "
28*4882a593Smuzhiyun 	" before undocking");
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun struct dock_station {
31*4882a593Smuzhiyun 	acpi_handle handle;
32*4882a593Smuzhiyun 	unsigned long last_dock_time;
33*4882a593Smuzhiyun 	u32 flags;
34*4882a593Smuzhiyun 	struct list_head dependent_devices;
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	struct list_head sibling;
37*4882a593Smuzhiyun 	struct platform_device *dock_device;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun static LIST_HEAD(dock_stations);
40*4882a593Smuzhiyun static int dock_station_count;
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun struct dock_dependent_device {
43*4882a593Smuzhiyun 	struct list_head list;
44*4882a593Smuzhiyun 	struct acpi_device *adev;
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #define DOCK_DOCKING	0x00000001
48*4882a593Smuzhiyun #define DOCK_UNDOCKING  0x00000002
49*4882a593Smuzhiyun #define DOCK_IS_DOCK	0x00000010
50*4882a593Smuzhiyun #define DOCK_IS_ATA	0x00000020
51*4882a593Smuzhiyun #define DOCK_IS_BAT	0x00000040
52*4882a593Smuzhiyun #define DOCK_EVENT	3
53*4882a593Smuzhiyun #define UNDOCK_EVENT	2
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun enum dock_callback_type {
56*4882a593Smuzhiyun 	DOCK_CALL_HANDLER,
57*4882a593Smuzhiyun 	DOCK_CALL_FIXUP,
58*4882a593Smuzhiyun 	DOCK_CALL_UEVENT,
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun /*****************************************************************************
62*4882a593Smuzhiyun  *                         Dock Dependent device functions                   *
63*4882a593Smuzhiyun  *****************************************************************************/
64*4882a593Smuzhiyun /**
65*4882a593Smuzhiyun  * add_dock_dependent_device - associate a device with the dock station
66*4882a593Smuzhiyun  * @ds: Dock station.
67*4882a593Smuzhiyun  * @adev: Dependent ACPI device object.
68*4882a593Smuzhiyun  *
69*4882a593Smuzhiyun  * Add the dependent device to the dock's dependent device list.
70*4882a593Smuzhiyun  */
add_dock_dependent_device(struct dock_station * ds,struct acpi_device * adev)71*4882a593Smuzhiyun static int add_dock_dependent_device(struct dock_station *ds,
72*4882a593Smuzhiyun 				     struct acpi_device *adev)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	struct dock_dependent_device *dd;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
77*4882a593Smuzhiyun 	if (!dd)
78*4882a593Smuzhiyun 		return -ENOMEM;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	dd->adev = adev;
81*4882a593Smuzhiyun 	INIT_LIST_HEAD(&dd->list);
82*4882a593Smuzhiyun 	list_add_tail(&dd->list, &ds->dependent_devices);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	return 0;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
dock_hotplug_event(struct dock_dependent_device * dd,u32 event,enum dock_callback_type cb_type)87*4882a593Smuzhiyun static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
88*4882a593Smuzhiyun 			       enum dock_callback_type cb_type)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun 	struct acpi_device *adev = dd->adev;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	acpi_lock_hp_context();
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	if (!adev->hp)
95*4882a593Smuzhiyun 		goto out;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	if (cb_type == DOCK_CALL_FIXUP) {
98*4882a593Smuzhiyun 		void (*fixup)(struct acpi_device *);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 		fixup = adev->hp->fixup;
101*4882a593Smuzhiyun 		if (fixup) {
102*4882a593Smuzhiyun 			acpi_unlock_hp_context();
103*4882a593Smuzhiyun 			fixup(adev);
104*4882a593Smuzhiyun 			return;
105*4882a593Smuzhiyun 		}
106*4882a593Smuzhiyun 	} else if (cb_type == DOCK_CALL_UEVENT) {
107*4882a593Smuzhiyun 		void (*uevent)(struct acpi_device *, u32);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 		uevent = adev->hp->uevent;
110*4882a593Smuzhiyun 		if (uevent) {
111*4882a593Smuzhiyun 			acpi_unlock_hp_context();
112*4882a593Smuzhiyun 			uevent(adev, event);
113*4882a593Smuzhiyun 			return;
114*4882a593Smuzhiyun 		}
115*4882a593Smuzhiyun 	} else {
116*4882a593Smuzhiyun 		int (*notify)(struct acpi_device *, u32);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 		notify = adev->hp->notify;
119*4882a593Smuzhiyun 		if (notify) {
120*4882a593Smuzhiyun 			acpi_unlock_hp_context();
121*4882a593Smuzhiyun 			notify(adev, event);
122*4882a593Smuzhiyun 			return;
123*4882a593Smuzhiyun 		}
124*4882a593Smuzhiyun 	}
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun  out:
127*4882a593Smuzhiyun 	acpi_unlock_hp_context();
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
find_dock_station(acpi_handle handle)130*4882a593Smuzhiyun static struct dock_station *find_dock_station(acpi_handle handle)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	struct dock_station *ds;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	list_for_each_entry(ds, &dock_stations, sibling)
135*4882a593Smuzhiyun 		if (ds->handle == handle)
136*4882a593Smuzhiyun 			return ds;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	return NULL;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun /**
142*4882a593Smuzhiyun  * find_dock_dependent_device - get a device dependent on this dock
143*4882a593Smuzhiyun  * @ds: the dock station
144*4882a593Smuzhiyun  * @adev: ACPI device object to find.
145*4882a593Smuzhiyun  *
146*4882a593Smuzhiyun  * iterate over the dependent device list for this dock.  If the
147*4882a593Smuzhiyun  * dependent device matches the handle, return.
148*4882a593Smuzhiyun  */
149*4882a593Smuzhiyun static struct dock_dependent_device *
find_dock_dependent_device(struct dock_station * ds,struct acpi_device * adev)150*4882a593Smuzhiyun find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	struct dock_dependent_device *dd;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	list_for_each_entry(dd, &ds->dependent_devices, list)
155*4882a593Smuzhiyun 		if (adev == dd->adev)
156*4882a593Smuzhiyun 			return dd;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	return NULL;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
register_dock_dependent_device(struct acpi_device * adev,acpi_handle dshandle)161*4882a593Smuzhiyun void register_dock_dependent_device(struct acpi_device *adev,
162*4882a593Smuzhiyun 				    acpi_handle dshandle)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	struct dock_station *ds = find_dock_station(dshandle);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	if (ds && !find_dock_dependent_device(ds, adev))
167*4882a593Smuzhiyun 		add_dock_dependent_device(ds, adev);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun /*****************************************************************************
171*4882a593Smuzhiyun  *                         Dock functions                                    *
172*4882a593Smuzhiyun  *****************************************************************************/
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun /**
175*4882a593Smuzhiyun  * is_dock_device - see if a device is on a dock station
176*4882a593Smuzhiyun  * @adev: ACPI device object to check.
177*4882a593Smuzhiyun  *
178*4882a593Smuzhiyun  * If this device is either the dock station itself,
179*4882a593Smuzhiyun  * or is a device dependent on the dock station, then it
180*4882a593Smuzhiyun  * is a dock device
181*4882a593Smuzhiyun  */
is_dock_device(struct acpi_device * adev)182*4882a593Smuzhiyun int is_dock_device(struct acpi_device *adev)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	struct dock_station *dock_station;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	if (!dock_station_count)
187*4882a593Smuzhiyun 		return 0;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	if (acpi_dock_match(adev->handle))
190*4882a593Smuzhiyun 		return 1;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	list_for_each_entry(dock_station, &dock_stations, sibling)
193*4882a593Smuzhiyun 		if (find_dock_dependent_device(dock_station, adev))
194*4882a593Smuzhiyun 			return 1;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	return 0;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(is_dock_device);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun /**
201*4882a593Smuzhiyun  * dock_present - see if the dock station is present.
202*4882a593Smuzhiyun  * @ds: the dock station
203*4882a593Smuzhiyun  *
204*4882a593Smuzhiyun  * execute the _STA method.  note that present does not
205*4882a593Smuzhiyun  * imply that we are docked.
206*4882a593Smuzhiyun  */
dock_present(struct dock_station * ds)207*4882a593Smuzhiyun static int dock_present(struct dock_station *ds)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	unsigned long long sta;
210*4882a593Smuzhiyun 	acpi_status status;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	if (ds) {
213*4882a593Smuzhiyun 		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
214*4882a593Smuzhiyun 		if (ACPI_SUCCESS(status) && sta)
215*4882a593Smuzhiyun 			return 1;
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 	return 0;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun /**
221*4882a593Smuzhiyun  * hot_remove_dock_devices - Remove dock station devices.
222*4882a593Smuzhiyun  * @ds: Dock station.
223*4882a593Smuzhiyun  */
hot_remove_dock_devices(struct dock_station * ds)224*4882a593Smuzhiyun static void hot_remove_dock_devices(struct dock_station *ds)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	struct dock_dependent_device *dd;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	/*
229*4882a593Smuzhiyun 	 * Walk the list in reverse order so that devices that have been added
230*4882a593Smuzhiyun 	 * last are removed first (in case there are some indirect dependencies
231*4882a593Smuzhiyun 	 * between them).
232*4882a593Smuzhiyun 	 */
233*4882a593Smuzhiyun 	list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
234*4882a593Smuzhiyun 		dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST,
235*4882a593Smuzhiyun 				   DOCK_CALL_HANDLER);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
238*4882a593Smuzhiyun 		acpi_bus_trim(dd->adev);
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun /**
242*4882a593Smuzhiyun  * hotplug_dock_devices - Insert devices on a dock station.
243*4882a593Smuzhiyun  * @ds: the dock station
244*4882a593Smuzhiyun  * @event: either bus check or device check request
245*4882a593Smuzhiyun  *
246*4882a593Smuzhiyun  * Some devices on the dock station need to have drivers called
247*4882a593Smuzhiyun  * to perform hotplug operations after a dock event has occurred.
248*4882a593Smuzhiyun  * Traverse the list of dock devices that have registered a
249*4882a593Smuzhiyun  * hotplug handler, and call the handler.
250*4882a593Smuzhiyun  */
hotplug_dock_devices(struct dock_station * ds,u32 event)251*4882a593Smuzhiyun static void hotplug_dock_devices(struct dock_station *ds, u32 event)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	struct dock_dependent_device *dd;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	/* Call driver specific post-dock fixups. */
256*4882a593Smuzhiyun 	list_for_each_entry(dd, &ds->dependent_devices, list)
257*4882a593Smuzhiyun 		dock_hotplug_event(dd, event, DOCK_CALL_FIXUP);
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	/* Call driver specific hotplug functions. */
260*4882a593Smuzhiyun 	list_for_each_entry(dd, &ds->dependent_devices, list)
261*4882a593Smuzhiyun 		dock_hotplug_event(dd, event, DOCK_CALL_HANDLER);
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	/*
264*4882a593Smuzhiyun 	 * Check if all devices have been enumerated already.  If not, run
265*4882a593Smuzhiyun 	 * acpi_bus_scan() for them and that will cause scan handlers to be
266*4882a593Smuzhiyun 	 * attached to device objects or acpi_drivers to be stopped/started if
267*4882a593Smuzhiyun 	 * they are present.
268*4882a593Smuzhiyun 	 */
269*4882a593Smuzhiyun 	list_for_each_entry(dd, &ds->dependent_devices, list) {
270*4882a593Smuzhiyun 		struct acpi_device *adev = dd->adev;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 		if (!acpi_device_enumerated(adev)) {
273*4882a593Smuzhiyun 			int ret = acpi_bus_scan(adev->handle);
274*4882a593Smuzhiyun 			if (ret)
275*4882a593Smuzhiyun 				dev_dbg(&adev->dev, "scan error %d\n", -ret);
276*4882a593Smuzhiyun 		}
277*4882a593Smuzhiyun 	}
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
dock_event(struct dock_station * ds,u32 event,int num)280*4882a593Smuzhiyun static void dock_event(struct dock_station *ds, u32 event, int num)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun 	struct device *dev = &ds->dock_device->dev;
283*4882a593Smuzhiyun 	char event_string[13];
284*4882a593Smuzhiyun 	char *envp[] = { event_string, NULL };
285*4882a593Smuzhiyun 	struct dock_dependent_device *dd;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	if (num == UNDOCK_EVENT)
288*4882a593Smuzhiyun 		sprintf(event_string, "EVENT=undock");
289*4882a593Smuzhiyun 	else
290*4882a593Smuzhiyun 		sprintf(event_string, "EVENT=dock");
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	/*
293*4882a593Smuzhiyun 	 * Indicate that the status of the dock station has
294*4882a593Smuzhiyun 	 * changed.
295*4882a593Smuzhiyun 	 */
296*4882a593Smuzhiyun 	if (num == DOCK_EVENT)
297*4882a593Smuzhiyun 		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	list_for_each_entry(dd, &ds->dependent_devices, list)
300*4882a593Smuzhiyun 		dock_hotplug_event(dd, event, DOCK_CALL_UEVENT);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	if (num != DOCK_EVENT)
303*4882a593Smuzhiyun 		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun /**
307*4882a593Smuzhiyun  * handle_dock - handle a dock event
308*4882a593Smuzhiyun  * @ds: the dock station
309*4882a593Smuzhiyun  * @dock: to dock, or undock - that is the question
310*4882a593Smuzhiyun  *
311*4882a593Smuzhiyun  * Execute the _DCK method in response to an acpi event
312*4882a593Smuzhiyun  */
handle_dock(struct dock_station * ds,int dock)313*4882a593Smuzhiyun static void handle_dock(struct dock_station *ds, int dock)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun 	acpi_status status;
316*4882a593Smuzhiyun 	struct acpi_object_list arg_list;
317*4882a593Smuzhiyun 	union acpi_object arg;
318*4882a593Smuzhiyun 	unsigned long long value;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	acpi_handle_info(ds->handle, "%s\n", dock ? "docking" : "undocking");
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	/* _DCK method has one argument */
323*4882a593Smuzhiyun 	arg_list.count = 1;
324*4882a593Smuzhiyun 	arg_list.pointer = &arg;
325*4882a593Smuzhiyun 	arg.type = ACPI_TYPE_INTEGER;
326*4882a593Smuzhiyun 	arg.integer.value = dock;
327*4882a593Smuzhiyun 	status = acpi_evaluate_integer(ds->handle, "_DCK", &arg_list, &value);
328*4882a593Smuzhiyun 	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
329*4882a593Smuzhiyun 		acpi_handle_err(ds->handle, "Failed to execute _DCK (0x%x)\n",
330*4882a593Smuzhiyun 				status);
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun 
dock(struct dock_station * ds)333*4882a593Smuzhiyun static inline void dock(struct dock_station *ds)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	handle_dock(ds, 1);
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun 
undock(struct dock_station * ds)338*4882a593Smuzhiyun static inline void undock(struct dock_station *ds)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun 	handle_dock(ds, 0);
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun 
begin_dock(struct dock_station * ds)343*4882a593Smuzhiyun static inline void begin_dock(struct dock_station *ds)
344*4882a593Smuzhiyun {
345*4882a593Smuzhiyun 	ds->flags |= DOCK_DOCKING;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun 
complete_dock(struct dock_station * ds)348*4882a593Smuzhiyun static inline void complete_dock(struct dock_station *ds)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun 	ds->flags &= ~(DOCK_DOCKING);
351*4882a593Smuzhiyun 	ds->last_dock_time = jiffies;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
begin_undock(struct dock_station * ds)354*4882a593Smuzhiyun static inline void begin_undock(struct dock_station *ds)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	ds->flags |= DOCK_UNDOCKING;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun 
complete_undock(struct dock_station * ds)359*4882a593Smuzhiyun static inline void complete_undock(struct dock_station *ds)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	ds->flags &= ~(DOCK_UNDOCKING);
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun /**
365*4882a593Smuzhiyun  * dock_in_progress - see if we are in the middle of handling a dock event
366*4882a593Smuzhiyun  * @ds: the dock station
367*4882a593Smuzhiyun  *
368*4882a593Smuzhiyun  * Sometimes while docking, false dock events can be sent to the driver
369*4882a593Smuzhiyun  * because good connections aren't made or some other reason.  Ignore these
370*4882a593Smuzhiyun  * if we are in the middle of doing something.
371*4882a593Smuzhiyun  */
dock_in_progress(struct dock_station * ds)372*4882a593Smuzhiyun static int dock_in_progress(struct dock_station *ds)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	if ((ds->flags & DOCK_DOCKING) ||
375*4882a593Smuzhiyun 	    time_before(jiffies, (ds->last_dock_time + HZ)))
376*4882a593Smuzhiyun 		return 1;
377*4882a593Smuzhiyun 	return 0;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun /**
381*4882a593Smuzhiyun  * handle_eject_request - handle an undock request checking for error conditions
382*4882a593Smuzhiyun  *
383*4882a593Smuzhiyun  * Check to make sure the dock device is still present, then undock and
384*4882a593Smuzhiyun  * hotremove all the devices that may need removing.
385*4882a593Smuzhiyun  */
handle_eject_request(struct dock_station * ds,u32 event)386*4882a593Smuzhiyun static int handle_eject_request(struct dock_station *ds, u32 event)
387*4882a593Smuzhiyun {
388*4882a593Smuzhiyun 	if (dock_in_progress(ds))
389*4882a593Smuzhiyun 		return -EBUSY;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	/*
392*4882a593Smuzhiyun 	 * here we need to generate the undock
393*4882a593Smuzhiyun 	 * event prior to actually doing the undock
394*4882a593Smuzhiyun 	 * so that the device struct still exists.
395*4882a593Smuzhiyun 	 * Also, even send the dock event if the
396*4882a593Smuzhiyun 	 * device is not present anymore
397*4882a593Smuzhiyun 	 */
398*4882a593Smuzhiyun 	dock_event(ds, event, UNDOCK_EVENT);
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	hot_remove_dock_devices(ds);
401*4882a593Smuzhiyun 	undock(ds);
402*4882a593Smuzhiyun 	acpi_evaluate_lck(ds->handle, 0);
403*4882a593Smuzhiyun 	acpi_evaluate_ej0(ds->handle);
404*4882a593Smuzhiyun 	if (dock_present(ds)) {
405*4882a593Smuzhiyun 		acpi_handle_err(ds->handle, "Unable to undock!\n");
406*4882a593Smuzhiyun 		return -EBUSY;
407*4882a593Smuzhiyun 	}
408*4882a593Smuzhiyun 	complete_undock(ds);
409*4882a593Smuzhiyun 	return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun /**
413*4882a593Smuzhiyun  * dock_notify - Handle ACPI dock notification.
414*4882a593Smuzhiyun  * @adev: Dock station's ACPI device object.
415*4882a593Smuzhiyun  * @event: Event code.
416*4882a593Smuzhiyun  *
417*4882a593Smuzhiyun  * If we are notified to dock, then check to see if the dock is
418*4882a593Smuzhiyun  * present and then dock.  Notify all drivers of the dock event,
419*4882a593Smuzhiyun  * and then hotplug and devices that may need hotplugging.
420*4882a593Smuzhiyun  */
dock_notify(struct acpi_device * adev,u32 event)421*4882a593Smuzhiyun int dock_notify(struct acpi_device *adev, u32 event)
422*4882a593Smuzhiyun {
423*4882a593Smuzhiyun 	acpi_handle handle = adev->handle;
424*4882a593Smuzhiyun 	struct dock_station *ds = find_dock_station(handle);
425*4882a593Smuzhiyun 	int surprise_removal = 0;
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	if (!ds)
428*4882a593Smuzhiyun 		return -ENODEV;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	/*
431*4882a593Smuzhiyun 	 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
432*4882a593Smuzhiyun 	 * is sent and _DCK is present, it is assumed to mean an undock
433*4882a593Smuzhiyun 	 * request.
434*4882a593Smuzhiyun 	 */
435*4882a593Smuzhiyun 	if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
436*4882a593Smuzhiyun 		event = ACPI_NOTIFY_EJECT_REQUEST;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	/*
439*4882a593Smuzhiyun 	 * dock station: BUS_CHECK - docked or surprise removal
440*4882a593Smuzhiyun 	 *		 DEVICE_CHECK - undocked
441*4882a593Smuzhiyun 	 * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
442*4882a593Smuzhiyun 	 *
443*4882a593Smuzhiyun 	 * To simplify event handling, dock dependent device handler always
444*4882a593Smuzhiyun 	 * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
445*4882a593Smuzhiyun 	 * ACPI_NOTIFY_EJECT_REQUEST for removal
446*4882a593Smuzhiyun 	 */
447*4882a593Smuzhiyun 	switch (event) {
448*4882a593Smuzhiyun 	case ACPI_NOTIFY_BUS_CHECK:
449*4882a593Smuzhiyun 	case ACPI_NOTIFY_DEVICE_CHECK:
450*4882a593Smuzhiyun 		if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {
451*4882a593Smuzhiyun 			begin_dock(ds);
452*4882a593Smuzhiyun 			dock(ds);
453*4882a593Smuzhiyun 			if (!dock_present(ds)) {
454*4882a593Smuzhiyun 				acpi_handle_err(handle, "Unable to dock!\n");
455*4882a593Smuzhiyun 				complete_dock(ds);
456*4882a593Smuzhiyun 				break;
457*4882a593Smuzhiyun 			}
458*4882a593Smuzhiyun 			hotplug_dock_devices(ds, event);
459*4882a593Smuzhiyun 			complete_dock(ds);
460*4882a593Smuzhiyun 			dock_event(ds, event, DOCK_EVENT);
461*4882a593Smuzhiyun 			acpi_evaluate_lck(ds->handle, 1);
462*4882a593Smuzhiyun 			acpi_update_all_gpes();
463*4882a593Smuzhiyun 			break;
464*4882a593Smuzhiyun 		}
465*4882a593Smuzhiyun 		if (dock_present(ds) || dock_in_progress(ds))
466*4882a593Smuzhiyun 			break;
467*4882a593Smuzhiyun 		/* This is a surprise removal */
468*4882a593Smuzhiyun 		surprise_removal = 1;
469*4882a593Smuzhiyun 		event = ACPI_NOTIFY_EJECT_REQUEST;
470*4882a593Smuzhiyun 		/* Fall back */
471*4882a593Smuzhiyun 		fallthrough;
472*4882a593Smuzhiyun 	case ACPI_NOTIFY_EJECT_REQUEST:
473*4882a593Smuzhiyun 		begin_undock(ds);
474*4882a593Smuzhiyun 		if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
475*4882a593Smuzhiyun 		   || surprise_removal)
476*4882a593Smuzhiyun 			handle_eject_request(ds, event);
477*4882a593Smuzhiyun 		else
478*4882a593Smuzhiyun 			dock_event(ds, event, UNDOCK_EVENT);
479*4882a593Smuzhiyun 		break;
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun 	return 0;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun /*
485*4882a593Smuzhiyun  * show_docked - read method for "docked" file in sysfs
486*4882a593Smuzhiyun  */
docked_show(struct device * dev,struct device_attribute * attr,char * buf)487*4882a593Smuzhiyun static ssize_t docked_show(struct device *dev,
488*4882a593Smuzhiyun 			   struct device_attribute *attr, char *buf)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun 	struct dock_station *dock_station = dev->platform_data;
491*4882a593Smuzhiyun 	struct acpi_device *adev = NULL;
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	acpi_bus_get_device(dock_station->handle, &adev);
494*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%u\n", acpi_device_enumerated(adev));
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun static DEVICE_ATTR_RO(docked);
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun /*
499*4882a593Smuzhiyun  * show_flags - read method for flags file in sysfs
500*4882a593Smuzhiyun  */
flags_show(struct device * dev,struct device_attribute * attr,char * buf)501*4882a593Smuzhiyun static ssize_t flags_show(struct device *dev,
502*4882a593Smuzhiyun 			  struct device_attribute *attr, char *buf)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun 	struct dock_station *dock_station = dev->platform_data;
505*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun static DEVICE_ATTR_RO(flags);
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun /*
511*4882a593Smuzhiyun  * write_undock - write method for "undock" file in sysfs
512*4882a593Smuzhiyun  */
undock_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)513*4882a593Smuzhiyun static ssize_t undock_store(struct device *dev, struct device_attribute *attr,
514*4882a593Smuzhiyun 			    const char *buf, size_t count)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun 	int ret;
517*4882a593Smuzhiyun 	struct dock_station *dock_station = dev->platform_data;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	if (!count)
520*4882a593Smuzhiyun 		return -EINVAL;
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	acpi_scan_lock_acquire();
523*4882a593Smuzhiyun 	begin_undock(dock_station);
524*4882a593Smuzhiyun 	ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
525*4882a593Smuzhiyun 	acpi_scan_lock_release();
526*4882a593Smuzhiyun 	return ret ? ret: count;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun static DEVICE_ATTR_WO(undock);
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun /*
531*4882a593Smuzhiyun  * show_dock_uid - read method for "uid" file in sysfs
532*4882a593Smuzhiyun  */
uid_show(struct device * dev,struct device_attribute * attr,char * buf)533*4882a593Smuzhiyun static ssize_t uid_show(struct device *dev,
534*4882a593Smuzhiyun 			struct device_attribute *attr, char *buf)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	unsigned long long lbuf;
537*4882a593Smuzhiyun 	struct dock_station *dock_station = dev->platform_data;
538*4882a593Smuzhiyun 	acpi_status status = acpi_evaluate_integer(dock_station->handle,
539*4882a593Smuzhiyun 					"_UID", NULL, &lbuf);
540*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
541*4882a593Smuzhiyun 	    return 0;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%llx\n", lbuf);
544*4882a593Smuzhiyun }
545*4882a593Smuzhiyun static DEVICE_ATTR_RO(uid);
546*4882a593Smuzhiyun 
type_show(struct device * dev,struct device_attribute * attr,char * buf)547*4882a593Smuzhiyun static ssize_t type_show(struct device *dev,
548*4882a593Smuzhiyun 			 struct device_attribute *attr, char *buf)
549*4882a593Smuzhiyun {
550*4882a593Smuzhiyun 	struct dock_station *dock_station = dev->platform_data;
551*4882a593Smuzhiyun 	char *type;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	if (dock_station->flags & DOCK_IS_DOCK)
554*4882a593Smuzhiyun 		type = "dock_station";
555*4882a593Smuzhiyun 	else if (dock_station->flags & DOCK_IS_ATA)
556*4882a593Smuzhiyun 		type = "ata_bay";
557*4882a593Smuzhiyun 	else if (dock_station->flags & DOCK_IS_BAT)
558*4882a593Smuzhiyun 		type = "battery_bay";
559*4882a593Smuzhiyun 	else
560*4882a593Smuzhiyun 		type = "unknown";
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%s\n", type);
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun static DEVICE_ATTR_RO(type);
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun static struct attribute *dock_attributes[] = {
567*4882a593Smuzhiyun 	&dev_attr_docked.attr,
568*4882a593Smuzhiyun 	&dev_attr_flags.attr,
569*4882a593Smuzhiyun 	&dev_attr_undock.attr,
570*4882a593Smuzhiyun 	&dev_attr_uid.attr,
571*4882a593Smuzhiyun 	&dev_attr_type.attr,
572*4882a593Smuzhiyun 	NULL
573*4882a593Smuzhiyun };
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun static const struct attribute_group dock_attribute_group = {
576*4882a593Smuzhiyun 	.attrs = dock_attributes
577*4882a593Smuzhiyun };
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun /**
580*4882a593Smuzhiyun  * acpi_dock_add - Add a new dock station
581*4882a593Smuzhiyun  * @adev: Dock station ACPI device object.
582*4882a593Smuzhiyun  *
583*4882a593Smuzhiyun  * allocated and initialize a new dock station device.
584*4882a593Smuzhiyun  */
acpi_dock_add(struct acpi_device * adev)585*4882a593Smuzhiyun void acpi_dock_add(struct acpi_device *adev)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun 	struct dock_station *dock_station, ds = { NULL, };
588*4882a593Smuzhiyun 	struct platform_device_info pdevinfo;
589*4882a593Smuzhiyun 	acpi_handle handle = adev->handle;
590*4882a593Smuzhiyun 	struct platform_device *dd;
591*4882a593Smuzhiyun 	int ret;
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	memset(&pdevinfo, 0, sizeof(pdevinfo));
594*4882a593Smuzhiyun 	pdevinfo.name = "dock";
595*4882a593Smuzhiyun 	pdevinfo.id = dock_station_count;
596*4882a593Smuzhiyun 	pdevinfo.fwnode = acpi_fwnode_handle(adev);
597*4882a593Smuzhiyun 	pdevinfo.data = &ds;
598*4882a593Smuzhiyun 	pdevinfo.size_data = sizeof(ds);
599*4882a593Smuzhiyun 	dd = platform_device_register_full(&pdevinfo);
600*4882a593Smuzhiyun 	if (IS_ERR(dd))
601*4882a593Smuzhiyun 		return;
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	dock_station = dd->dev.platform_data;
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	dock_station->handle = handle;
606*4882a593Smuzhiyun 	dock_station->dock_device = dd;
607*4882a593Smuzhiyun 	dock_station->last_dock_time = jiffies - HZ;
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	INIT_LIST_HEAD(&dock_station->sibling);
610*4882a593Smuzhiyun 	INIT_LIST_HEAD(&dock_station->dependent_devices);
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	/* we want the dock device to send uevents */
613*4882a593Smuzhiyun 	dev_set_uevent_suppress(&dd->dev, 0);
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	if (acpi_dock_match(handle))
616*4882a593Smuzhiyun 		dock_station->flags |= DOCK_IS_DOCK;
617*4882a593Smuzhiyun 	if (acpi_ata_match(handle))
618*4882a593Smuzhiyun 		dock_station->flags |= DOCK_IS_ATA;
619*4882a593Smuzhiyun 	if (acpi_device_is_battery(adev))
620*4882a593Smuzhiyun 		dock_station->flags |= DOCK_IS_BAT;
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
623*4882a593Smuzhiyun 	if (ret)
624*4882a593Smuzhiyun 		goto err_unregister;
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	/* add the dock station as a device dependent on itself */
627*4882a593Smuzhiyun 	ret = add_dock_dependent_device(dock_station, adev);
628*4882a593Smuzhiyun 	if (ret)
629*4882a593Smuzhiyun 		goto err_rmgroup;
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 	dock_station_count++;
632*4882a593Smuzhiyun 	list_add(&dock_station->sibling, &dock_stations);
633*4882a593Smuzhiyun 	adev->flags.is_dock_station = true;
634*4882a593Smuzhiyun 	dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",
635*4882a593Smuzhiyun 		 dock_station_count);
636*4882a593Smuzhiyun 	return;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun err_rmgroup:
639*4882a593Smuzhiyun 	sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun err_unregister:
642*4882a593Smuzhiyun 	platform_device_unregister(dd);
643*4882a593Smuzhiyun 	acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
644*4882a593Smuzhiyun }
645