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