]>
Commit | Line | Data |
---|---|---|
eb50fd3a | 1 | // SPDX-License-Identifier: GPL-2.0 |
5a781787 VH |
2 | /* |
3 | * Arche Platform driver to control APB. | |
4 | * | |
5 | * Copyright 2014-2015 Google Inc. | |
6 | * Copyright 2014-2015 Linaro Ltd. | |
7 | * | |
8 | * Released under the GPLv2 only. | |
9 | */ | |
10 | ||
3b858df0 | 11 | #include <linux/clk.h> |
5a781787 | 12 | #include <linux/delay.h> |
5a781787 | 13 | #include <linux/gpio.h> |
3b858df0 | 14 | #include <linux/interrupt.h> |
5a781787 VH |
15 | #include <linux/of_gpio.h> |
16 | #include <linux/of_irq.h> | |
3b858df0 | 17 | #include <linux/module.h> |
5a781787 | 18 | #include <linux/pinctrl/consumer.h> |
3b858df0 VK |
19 | #include <linux/platform_device.h> |
20 | #include <linux/pm.h> | |
21 | #include <linux/regulator/consumer.h> | |
22 | #include <linux/spinlock.h> | |
1e5dd1f8 | 23 | #include "arche_platform.h" |
5a781787 | 24 | |
5a781787 | 25 | |
1e029b83 JH |
26 | static void apb_bootret_deassert(struct device *dev); |
27 | ||
5a781787 VH |
28 | struct arche_apb_ctrl_drvdata { |
29 | /* Control GPIO signals to and from AP <=> AP Bridges */ | |
5a781787 VH |
30 | int resetn_gpio; |
31 | int boot_ret_gpio; | |
32 | int pwroff_gpio; | |
33 | int wake_in_gpio; | |
34 | int wake_out_gpio; | |
35 | int pwrdn_gpio; | |
36 | ||
a821adb4 | 37 | enum arche_platform_state state; |
af3aae10 | 38 | bool init_disabled; |
5a781787 VH |
39 | |
40 | struct regulator *vcore; | |
41 | struct regulator *vio; | |
42 | ||
7fe93014 | 43 | int clk_en_gpio; |
5a781787 VH |
44 | struct clk *clk; |
45 | ||
46 | struct pinctrl *pinctrl; | |
47 | struct pinctrl_state *pin_default; | |
921dbe52 VH |
48 | |
49 | /* V2: SPI Bus control */ | |
50 | int spi_en_gpio; | |
51 | bool spi_en_polarity_high; | |
5a781787 VH |
52 | }; |
53 | ||
54 | /* | |
55 | * Note that these low level api's are active high | |
56 | */ | |
f1e9cbd5 | 57 | static inline void deassert_reset(unsigned int gpio) |
5a781787 VH |
58 | { |
59 | gpio_set_value(gpio, 1); | |
5a781787 VH |
60 | } |
61 | ||
f1e9cbd5 | 62 | static inline void assert_reset(unsigned int gpio) |
5a781787 VH |
63 | { |
64 | gpio_set_value(gpio, 0); | |
65 | } | |
66 | ||
5a781787 VH |
67 | /* |
68 | * Note: Please do not modify the below sequence, as it is as per the spec | |
69 | */ | |
80a057aa | 70 | static int coldboot_seq(struct platform_device *pdev) |
5a781787 VH |
71 | { |
72 | struct device *dev = &pdev->dev; | |
6961e046 | 73 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); |
5a781787 VH |
74 | int ret; |
75 | ||
c5e7cbaf VH |
76 | if (apb->init_disabled || |
77 | apb->state == ARCHE_PLATFORM_STATE_ACTIVE) | |
af3aae10 VH |
78 | return 0; |
79 | ||
5a781787 | 80 | /* Hold APB in reset state */ |
6ceed512 | 81 | assert_reset(apb->resetn_gpio); |
5a781787 | 82 | |
921dbe52 VH |
83 | if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && |
84 | gpio_is_valid(apb->spi_en_gpio)) | |
85 | devm_gpio_free(dev, apb->spi_en_gpio); | |
86 | ||
5a781787 | 87 | /* Enable power to APB */ |
dcf77c39 | 88 | if (!IS_ERR(apb->vcore)) { |
5a781787 VH |
89 | ret = regulator_enable(apb->vcore); |
90 | if (ret) { | |
91 | dev_err(dev, "failed to enable core regulator\n"); | |
92 | return ret; | |
93 | } | |
94 | } | |
aa187d33 | 95 | |
dcf77c39 | 96 | if (!IS_ERR(apb->vio)) { |
5a781787 VH |
97 | ret = regulator_enable(apb->vio); |
98 | if (ret) { | |
99 | dev_err(dev, "failed to enable IO regulator\n"); | |
6ceed512 | 100 | return ret; |
5a781787 VH |
101 | } |
102 | } | |
103 | ||
970dc85b | 104 | apb_bootret_deassert(dev); |
aa187d33 VH |
105 | |
106 | /* On DB3 clock was not mandatory */ | |
6ceed512 VH |
107 | if (gpio_is_valid(apb->clk_en_gpio)) |
108 | gpio_set_value(apb->clk_en_gpio, 1); | |
aa187d33 VH |
109 | |
110 | usleep_range(100, 200); | |
5a781787 | 111 | |
6ceed512 VH |
112 | /* deassert reset to APB : Active-low signal */ |
113 | deassert_reset(apb->resetn_gpio); | |
d258432f | 114 | |
6ceed512 | 115 | apb->state = ARCHE_PLATFORM_STATE_ACTIVE; |
5a781787 | 116 | |
6ceed512 | 117 | return 0; |
5a781787 VH |
118 | } |
119 | ||
80a057aa | 120 | static int fw_flashing_seq(struct platform_device *pdev) |
33d76291 VH |
121 | { |
122 | struct device *dev = &pdev->dev; | |
123 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); | |
124 | int ret; | |
125 | ||
c5e7cbaf VH |
126 | if (apb->init_disabled || |
127 | apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) | |
af3aae10 VH |
128 | return 0; |
129 | ||
33d76291 VH |
130 | ret = regulator_enable(apb->vcore); |
131 | if (ret) { | |
132 | dev_err(dev, "failed to enable core regulator\n"); | |
133 | return ret; | |
134 | } | |
135 | ||
136 | ret = regulator_enable(apb->vio); | |
137 | if (ret) { | |
138 | dev_err(dev, "failed to enable IO regulator\n"); | |
139 | return ret; | |
140 | } | |
141 | ||
921dbe52 VH |
142 | if (gpio_is_valid(apb->spi_en_gpio)) { |
143 | unsigned long flags; | |
144 | ||
145 | if (apb->spi_en_polarity_high) | |
146 | flags = GPIOF_OUT_INIT_HIGH; | |
147 | else | |
148 | flags = GPIOF_OUT_INIT_LOW; | |
149 | ||
150 | ret = devm_gpio_request_one(dev, apb->spi_en_gpio, | |
151 | flags, "apb_spi_en"); | |
152 | if (ret) { | |
153 | dev_err(dev, "Failed requesting SPI bus en gpio %d\n", | |
154 | apb->spi_en_gpio); | |
155 | return ret; | |
156 | } | |
157 | } | |
158 | ||
33d76291 VH |
159 | /* for flashing device should be in reset state */ |
160 | assert_reset(apb->resetn_gpio); | |
161 | apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING; | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
80a057aa | 166 | static int standby_boot_seq(struct platform_device *pdev) |
33d76291 | 167 | { |
921dbe52 | 168 | struct device *dev = &pdev->dev; |
33d76291 VH |
169 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); |
170 | ||
af3aae10 VH |
171 | if (apb->init_disabled) |
172 | return 0; | |
173 | ||
461ab807 GK |
174 | /* |
175 | * Even if it is in OFF state, | |
176 | * then we do not want to change the state | |
177 | */ | |
c5e7cbaf VH |
178 | if (apb->state == ARCHE_PLATFORM_STATE_STANDBY || |
179 | apb->state == ARCHE_PLATFORM_STATE_OFF) | |
33d76291 VH |
180 | return 0; |
181 | ||
921dbe52 VH |
182 | if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && |
183 | gpio_is_valid(apb->spi_en_gpio)) | |
184 | devm_gpio_free(dev, apb->spi_en_gpio); | |
185 | ||
33d76291 VH |
186 | /* |
187 | * As per WDM spec, do nothing | |
188 | * | |
189 | * Pasted from WDM spec, | |
190 | * - A falling edge on POWEROFF_L is detected (a) | |
191 | * - WDM enters standby mode, but no output signals are changed | |
1047cc13 | 192 | */ |
33d76291 VH |
193 | |
194 | /* TODO: POWEROFF_L is input to WDM module */ | |
195 | apb->state = ARCHE_PLATFORM_STATE_STANDBY; | |
196 | return 0; | |
197 | } | |
198 | ||
80a057aa | 199 | static void poweroff_seq(struct platform_device *pdev) |
5667ab17 | 200 | { |
921dbe52 | 201 | struct device *dev = &pdev->dev; |
6961e046 VH |
202 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); |
203 | ||
c5e7cbaf | 204 | if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF) |
af3aae10 VH |
205 | return; |
206 | ||
921dbe52 VH |
207 | if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && |
208 | gpio_is_valid(apb->spi_en_gpio)) | |
209 | devm_gpio_free(dev, apb->spi_en_gpio); | |
210 | ||
5667ab17 VH |
211 | /* disable the clock */ |
212 | if (gpio_is_valid(apb->clk_en_gpio)) | |
213 | gpio_set_value(apb->clk_en_gpio, 0); | |
214 | ||
215 | if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) | |
216 | regulator_disable(apb->vcore); | |
217 | ||
218 | if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0) | |
219 | regulator_disable(apb->vio); | |
220 | ||
221 | /* As part of exit, put APB back in reset state */ | |
222 | assert_reset(apb->resetn_gpio); | |
223 | apb->state = ARCHE_PLATFORM_STATE_OFF; | |
224 | ||
225 | /* TODO: May have to send an event to SVC about this exit */ | |
226 | } | |
227 | ||
1e029b83 | 228 | static void apb_bootret_deassert(struct device *dev) |
970dc85b BD |
229 | { |
230 | struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); | |
231 | ||
232 | gpio_set_value(apb->boot_ret_gpio, 0); | |
233 | } | |
234 | ||
65fd5a50 VH |
235 | int apb_ctrl_coldboot(struct device *dev) |
236 | { | |
237 | return coldboot_seq(to_platform_device(dev)); | |
238 | } | |
239 | ||
240 | int apb_ctrl_fw_flashing(struct device *dev) | |
241 | { | |
242 | return fw_flashing_seq(to_platform_device(dev)); | |
243 | } | |
244 | ||
245 | int apb_ctrl_standby_boot(struct device *dev) | |
246 | { | |
247 | return standby_boot_seq(to_platform_device(dev)); | |
248 | } | |
249 | ||
250 | void apb_ctrl_poweroff(struct device *dev) | |
251 | { | |
252 | poweroff_seq(to_platform_device(dev)); | |
253 | } | |
254 | ||
33d76291 VH |
255 | static ssize_t state_store(struct device *dev, |
256 | struct device_attribute *attr, const char *buf, size_t count) | |
257 | { | |
258 | struct platform_device *pdev = to_platform_device(dev); | |
259 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); | |
260 | int ret = 0; | |
af3aae10 | 261 | bool is_disabled; |
33d76291 VH |
262 | |
263 | if (sysfs_streq(buf, "off")) { | |
264 | if (apb->state == ARCHE_PLATFORM_STATE_OFF) | |
265 | return count; | |
266 | ||
80a057aa | 267 | poweroff_seq(pdev); |
33d76291 VH |
268 | } else if (sysfs_streq(buf, "active")) { |
269 | if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE) | |
270 | return count; | |
271 | ||
80a057aa | 272 | poweroff_seq(pdev); |
af3aae10 VH |
273 | is_disabled = apb->init_disabled; |
274 | apb->init_disabled = false; | |
80a057aa | 275 | ret = coldboot_seq(pdev); |
af3aae10 VH |
276 | if (ret) |
277 | apb->init_disabled = is_disabled; | |
33d76291 VH |
278 | } else if (sysfs_streq(buf, "standby")) { |
279 | if (apb->state == ARCHE_PLATFORM_STATE_STANDBY) | |
280 | return count; | |
281 | ||
80a057aa | 282 | ret = standby_boot_seq(pdev); |
33d76291 VH |
283 | } else if (sysfs_streq(buf, "fw_flashing")) { |
284 | if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) | |
285 | return count; | |
286 | ||
cb026e39 SG |
287 | /* |
288 | * First we want to make sure we power off everything | |
289 | * and then enter FW flashing state | |
290 | */ | |
80a057aa VH |
291 | poweroff_seq(pdev); |
292 | ret = fw_flashing_seq(pdev); | |
33d76291 VH |
293 | } else { |
294 | dev_err(dev, "unknown state\n"); | |
295 | ret = -EINVAL; | |
296 | } | |
297 | ||
298 | return ret ? ret : count; | |
299 | } | |
300 | ||
301 | static ssize_t state_show(struct device *dev, | |
302 | struct device_attribute *attr, char *buf) | |
303 | { | |
304 | struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); | |
305 | ||
306 | switch (apb->state) { | |
307 | case ARCHE_PLATFORM_STATE_OFF: | |
af3aae10 VH |
308 | return sprintf(buf, "off%s\n", |
309 | apb->init_disabled ? ",disabled" : ""); | |
33d76291 VH |
310 | case ARCHE_PLATFORM_STATE_ACTIVE: |
311 | return sprintf(buf, "active\n"); | |
312 | case ARCHE_PLATFORM_STATE_STANDBY: | |
313 | return sprintf(buf, "standby\n"); | |
314 | case ARCHE_PLATFORM_STATE_FW_FLASHING: | |
315 | return sprintf(buf, "fw_flashing\n"); | |
316 | default: | |
317 | return sprintf(buf, "unknown state\n"); | |
318 | } | |
319 | } | |
320 | ||
321 | static DEVICE_ATTR_RW(state); | |
322 | ||
5a781787 VH |
323 | static int apb_ctrl_get_devtree_data(struct platform_device *pdev, |
324 | struct arche_apb_ctrl_drvdata *apb) | |
325 | { | |
326 | struct device *dev = &pdev->dev; | |
327 | struct device_node *np = dev->of_node; | |
6ceed512 | 328 | int ret; |
5a781787 | 329 | |
5a781787 | 330 | apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); |
7541c1a1 | 331 | if (apb->resetn_gpio < 0) { |
5a781787 VH |
332 | dev_err(dev, "failed to get reset gpio\n"); |
333 | return apb->resetn_gpio; | |
334 | } | |
6ceed512 VH |
335 | ret = devm_gpio_request_one(dev, apb->resetn_gpio, |
336 | GPIOF_OUT_INIT_LOW, "apb-reset"); | |
337 | if (ret) { | |
338 | dev_err(dev, "Failed requesting reset gpio %d\n", | |
339 | apb->resetn_gpio); | |
340 | return ret; | |
341 | } | |
5a781787 VH |
342 | |
343 | apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0); | |
7541c1a1 | 344 | if (apb->boot_ret_gpio < 0) { |
5a781787 VH |
345 | dev_err(dev, "failed to get boot retention gpio\n"); |
346 | return apb->boot_ret_gpio; | |
347 | } | |
6ceed512 VH |
348 | ret = devm_gpio_request_one(dev, apb->boot_ret_gpio, |
349 | GPIOF_OUT_INIT_LOW, "boot retention"); | |
350 | if (ret) { | |
351 | dev_err(dev, "Failed requesting bootret gpio %d\n", | |
352 | apb->boot_ret_gpio); | |
353 | return ret; | |
354 | } | |
5a781787 VH |
355 | |
356 | /* It's not mandatory to support power management interface */ | |
357 | apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); | |
7541c1a1 | 358 | if (apb->pwroff_gpio < 0) { |
db0cff55 | 359 | dev_err(dev, "failed to get power off gpio\n"); |
5a781787 VH |
360 | return apb->pwroff_gpio; |
361 | } | |
6ceed512 VH |
362 | ret = devm_gpio_request_one(dev, apb->pwroff_gpio, |
363 | GPIOF_IN, "pwroff_n"); | |
364 | if (ret) { | |
365 | dev_err(dev, "Failed requesting pwroff_n gpio %d\n", | |
366 | apb->pwroff_gpio); | |
367 | return ret; | |
368 | } | |
5a781787 VH |
369 | |
370 | /* Do not make clock mandatory as of now (for DB3) */ | |
371 | apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); | |
6ceed512 | 372 | if (apb->clk_en_gpio < 0) { |
db0cff55 | 373 | dev_warn(dev, "failed to get clock en gpio\n"); |
6ceed512 VH |
374 | } else if (gpio_is_valid(apb->clk_en_gpio)) { |
375 | ret = devm_gpio_request_one(dev, apb->clk_en_gpio, | |
376 | GPIOF_OUT_INIT_LOW, "apb_clk_en"); | |
377 | if (ret) { | |
378 | dev_warn(dev, "Failed requesting APB clock en gpio %d\n", | |
379 | apb->clk_en_gpio); | |
380 | return ret; | |
381 | } | |
382 | } | |
5a781787 VH |
383 | |
384 | apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); | |
7541c1a1 | 385 | if (apb->pwrdn_gpio < 0) |
db0cff55 | 386 | dev_warn(dev, "failed to get power down gpio\n"); |
5a781787 VH |
387 | |
388 | /* Regulators are optional, as we may have fixed supply coming in */ | |
389 | apb->vcore = devm_regulator_get(dev, "vcore"); | |
dcf77c39 | 390 | if (IS_ERR(apb->vcore)) |
db0cff55 | 391 | dev_warn(dev, "no core regulator found\n"); |
5a781787 VH |
392 | |
393 | apb->vio = devm_regulator_get(dev, "vio"); | |
dcf77c39 | 394 | if (IS_ERR(apb->vio)) |
db0cff55 | 395 | dev_warn(dev, "no IO regulator found\n"); |
5a781787 VH |
396 | |
397 | apb->pinctrl = devm_pinctrl_get(&pdev->dev); | |
398 | if (IS_ERR(apb->pinctrl)) { | |
399 | dev_err(&pdev->dev, "could not get pinctrl handle\n"); | |
400 | return PTR_ERR(apb->pinctrl); | |
401 | } | |
402 | apb->pin_default = pinctrl_lookup_state(apb->pinctrl, "default"); | |
403 | if (IS_ERR(apb->pin_default)) { | |
404 | dev_err(&pdev->dev, "could not get default pin state\n"); | |
405 | return PTR_ERR(apb->pin_default); | |
406 | } | |
407 | ||
921dbe52 VH |
408 | /* Only applicable for platform >= V2 */ |
409 | apb->spi_en_gpio = of_get_named_gpio(np, "spi-en-gpio", 0); | |
410 | if (apb->spi_en_gpio >= 0) { | |
411 | if (of_property_read_bool(pdev->dev.of_node, | |
412 | "spi-en-active-high")) | |
413 | apb->spi_en_polarity_high = true; | |
414 | } | |
415 | ||
5a781787 VH |
416 | return 0; |
417 | } | |
418 | ||
7b62b61c | 419 | static int arche_apb_ctrl_probe(struct platform_device *pdev) |
5a781787 VH |
420 | { |
421 | int ret; | |
422 | struct arche_apb_ctrl_drvdata *apb; | |
423 | struct device *dev = &pdev->dev; | |
424 | ||
425 | apb = devm_kzalloc(&pdev->dev, sizeof(*apb), GFP_KERNEL); | |
426 | if (!apb) | |
427 | return -ENOMEM; | |
428 | ||
429 | ret = apb_ctrl_get_devtree_data(pdev, apb); | |
430 | if (ret) { | |
431 | dev_err(dev, "failed to get apb devicetree data %d\n", ret); | |
432 | return ret; | |
433 | } | |
434 | ||
f2222a41 VH |
435 | /* Initially set APB to OFF state */ |
436 | apb->state = ARCHE_PLATFORM_STATE_OFF; | |
af3aae10 | 437 | /* Check whether device needs to be enabled on boot */ |
20de72f5 | 438 | if (of_property_read_bool(pdev->dev.of_node, "arche,init-disable")) |
af3aae10 | 439 | apb->init_disabled = true; |
f2222a41 | 440 | |
6961e046 VH |
441 | platform_set_drvdata(pdev, apb); |
442 | ||
33d76291 VH |
443 | /* Create sysfs interface to allow user to change state dynamically */ |
444 | ret = device_create_file(dev, &dev_attr_state); | |
445 | if (ret) { | |
446 | dev_err(dev, "failed to create state file in sysfs\n"); | |
447 | return ret; | |
448 | } | |
449 | ||
5a781787 VH |
450 | dev_info(&pdev->dev, "Device registered successfully\n"); |
451 | return 0; | |
5a781787 VH |
452 | } |
453 | ||
7b62b61c | 454 | static int arche_apb_ctrl_remove(struct platform_device *pdev) |
5a781787 | 455 | { |
33d76291 | 456 | device_remove_file(&pdev->dev, &dev_attr_state); |
80a057aa | 457 | poweroff_seq(pdev); |
5a781787 VH |
458 | platform_set_drvdata(pdev, NULL); |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
6da80863 | 463 | static int __maybe_unused arche_apb_ctrl_suspend(struct device *dev) |
5a781787 VH |
464 | { |
465 | /* | |
3b538c39 | 466 | * If timing profile permits, we may shutdown bridge |
5a781787 VH |
467 | * completely |
468 | * | |
469 | * TODO: sequence ?? | |
470 | * | |
471 | * Also, need to make sure we meet precondition for unipro suspend | |
472 | * Precondition: Definition ??? | |
473 | */ | |
474 | return 0; | |
475 | } | |
476 | ||
6da80863 | 477 | static int __maybe_unused arche_apb_ctrl_resume(struct device *dev) |
5a781787 VH |
478 | { |
479 | /* | |
480 | * Atleast for ES2 we have to meet the delay requirement between | |
481 | * unipro switch and AP bridge init, depending on whether bridge is in | |
482 | * OFF state or standby state. | |
483 | * | |
484 | * Based on whether bridge is in standby or OFF state we may have to | |
485 | * assert multiple signals. Please refer to WDM spec, for more info. | |
486 | * | |
487 | */ | |
488 | return 0; | |
489 | } | |
490 | ||
1f77b363 DL |
491 | static void arche_apb_ctrl_shutdown(struct platform_device *pdev) |
492 | { | |
493 | apb_ctrl_poweroff(&pdev->dev); | |
494 | } | |
495 | ||
7b62b61c VK |
496 | static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend, |
497 | arche_apb_ctrl_resume); | |
498 | ||
5c925fe2 | 499 | static const struct of_device_id arche_apb_ctrl_of_match[] = { |
7b62b61c VK |
500 | { .compatible = "usbffff,2", }, |
501 | { }, | |
502 | }; | |
503 | ||
504 | static struct platform_driver arche_apb_ctrl_device_driver = { | |
505 | .probe = arche_apb_ctrl_probe, | |
506 | .remove = arche_apb_ctrl_remove, | |
1f77b363 | 507 | .shutdown = arche_apb_ctrl_shutdown, |
7b62b61c VK |
508 | .driver = { |
509 | .name = "arche-apb-ctrl", | |
510 | .pm = &arche_apb_ctrl_pm_ops, | |
511 | .of_match_table = arche_apb_ctrl_of_match, | |
512 | } | |
513 | }; | |
514 | ||
515 | int __init arche_apb_init(void) | |
516 | { | |
517 | return platform_driver_register(&arche_apb_ctrl_device_driver); | |
518 | } | |
519 | ||
520 | void __exit arche_apb_exit(void) | |
521 | { | |
522 | platform_driver_unregister(&arche_apb_ctrl_device_driver); | |
523 | } |