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