Commit | Line | Data |
---|---|---|
4ab6174e SG |
1 | /* |
2 | * ChromeOS EC multi-function device | |
3 | * | |
4 | * Copyright (C) 2012 Google, Inc | |
5 | * | |
6 | * This software is licensed under the terms of the GNU General Public | |
7 | * License version 2, as published by the Free Software Foundation, and | |
8 | * may be copied, distributed, and modified under those terms. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * The ChromeOS EC multi function device is used to mux all the requests | |
16 | * to the EC device for its multiple features: keyboard controller, | |
17 | * battery charging and regulator control, firmware update. | |
18 | */ | |
19 | ||
bb03ffb9 | 20 | #include <linux/of_platform.h> |
4ab6174e SG |
21 | #include <linux/interrupt.h> |
22 | #include <linux/slab.h> | |
5ebeaff5 | 23 | #include <linux/module.h> |
4ab6174e SG |
24 | #include <linux/mfd/core.h> |
25 | #include <linux/mfd/cros_ec.h> | |
a6551a76 | 26 | |
57b33ff0 GG |
27 | #define CROS_EC_DEV_EC_INDEX 0 |
28 | #define CROS_EC_DEV_PD_INDEX 1 | |
29 | ||
cf649e00 | 30 | static struct cros_ec_platform ec_p = { |
57b33ff0 GG |
31 | .ec_name = CROS_EC_DEV_NAME, |
32 | .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX), | |
33 | }; | |
34 | ||
cf649e00 | 35 | static struct cros_ec_platform pd_p = { |
57b33ff0 GG |
36 | .ec_name = CROS_EC_DEV_PD_NAME, |
37 | .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX), | |
38 | }; | |
39 | ||
cf649e00 | 40 | static const struct mfd_cell ec_cell = { |
57b33ff0 GG |
41 | .name = "cros-ec-ctl", |
42 | .platform_data = &ec_p, | |
43 | .pdata_size = sizeof(ec_p), | |
44 | }; | |
45 | ||
cf649e00 | 46 | static const struct mfd_cell ec_pd_cell = { |
57b33ff0 GG |
47 | .name = "cros-ec-ctl", |
48 | .platform_data = &pd_p, | |
49 | .pdata_size = sizeof(pd_p), | |
4ab6174e SG |
50 | }; |
51 | ||
52 | int cros_ec_register(struct cros_ec_device *ec_dev) | |
53 | { | |
54 | struct device *dev = ec_dev->dev; | |
55 | int err = 0; | |
56 | ||
2c7589af SB |
57 | ec_dev->max_request = sizeof(struct ec_params_hello); |
58 | ec_dev->max_response = sizeof(struct ec_response_get_protocol_info); | |
59 | ec_dev->max_passthru = 0; | |
60 | ||
61 | ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); | |
62 | if (!ec_dev->din) | |
63 | return -ENOMEM; | |
64 | ||
65 | ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); | |
66 | if (!ec_dev->dout) | |
67 | return -ENOMEM; | |
4ab6174e | 68 | |
63427530 AB |
69 | mutex_init(&ec_dev->lock); |
70 | ||
2c7589af SB |
71 | cros_ec_query_all(ec_dev); |
72 | ||
57b33ff0 | 73 | err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, |
4ab6174e SG |
74 | NULL, ec_dev->irq, NULL); |
75 | if (err) { | |
57b33ff0 GG |
76 | dev_err(dev, |
77 | "Failed to register Embedded Controller subdevice %d\n", | |
78 | err); | |
d1fd345e | 79 | return err; |
4ab6174e SG |
80 | } |
81 | ||
57b33ff0 GG |
82 | if (ec_dev->max_passthru) { |
83 | /* | |
84 | * Register a PD device as well on top of this device. | |
85 | * We make the following assumptions: | |
86 | * - behind an EC, we have a pd | |
87 | * - only one device added. | |
88 | * - the EC is responsive at init time (it is not true for a | |
89 | * sensor hub. | |
90 | */ | |
91 | err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, | |
92 | &ec_pd_cell, 1, NULL, ec_dev->irq, NULL); | |
93 | if (err) { | |
94 | dev_err(dev, | |
95 | "Failed to register Power Delivery subdevice %d\n", | |
96 | err); | |
97 | return err; | |
98 | } | |
99 | } | |
100 | ||
bb03ffb9 TB |
101 | if (IS_ENABLED(CONFIG_OF) && dev->of_node) { |
102 | err = of_platform_populate(dev->of_node, NULL, NULL, dev); | |
103 | if (err) { | |
104 | mfd_remove_devices(dev); | |
105 | dev_err(dev, "Failed to register sub-devices\n"); | |
106 | return err; | |
107 | } | |
108 | } | |
109 | ||
533cec8f | 110 | dev_info(dev, "Chrome EC device registered\n"); |
4ab6174e SG |
111 | |
112 | return 0; | |
4ab6174e | 113 | } |
5ebeaff5 | 114 | EXPORT_SYMBOL(cros_ec_register); |
4ab6174e SG |
115 | |
116 | int cros_ec_remove(struct cros_ec_device *ec_dev) | |
117 | { | |
118 | mfd_remove_devices(ec_dev->dev); | |
4ab6174e SG |
119 | |
120 | return 0; | |
121 | } | |
5ebeaff5 | 122 | EXPORT_SYMBOL(cros_ec_remove); |
4ab6174e SG |
123 | |
124 | #ifdef CONFIG_PM_SLEEP | |
125 | int cros_ec_suspend(struct cros_ec_device *ec_dev) | |
126 | { | |
127 | struct device *dev = ec_dev->dev; | |
128 | ||
129 | if (device_may_wakeup(dev)) | |
130 | ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); | |
131 | ||
132 | disable_irq(ec_dev->irq); | |
133 | ec_dev->was_wake_device = ec_dev->wake_enabled; | |
134 | ||
135 | return 0; | |
136 | } | |
5ebeaff5 | 137 | EXPORT_SYMBOL(cros_ec_suspend); |
4ab6174e SG |
138 | |
139 | int cros_ec_resume(struct cros_ec_device *ec_dev) | |
140 | { | |
141 | enable_irq(ec_dev->irq); | |
142 | ||
143 | if (ec_dev->wake_enabled) { | |
144 | disable_irq_wake(ec_dev->irq); | |
145 | ec_dev->wake_enabled = 0; | |
146 | } | |
147 | ||
148 | return 0; | |
149 | } | |
5ebeaff5 SO |
150 | EXPORT_SYMBOL(cros_ec_resume); |
151 | ||
4ab6174e | 152 | #endif |
a865a589 BR |
153 | |
154 | MODULE_LICENSE("GPL"); | |
155 | MODULE_DESCRIPTION("ChromeOS EC core driver"); |