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