1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /*************************************************************************** |
3 | * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com> * |
4 | * * |
5 | ***************************************************************************/ |
6 | |
7 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/init.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/regmap.h> |
13 | #include <linux/err.h> |
14 | #include <linux/io.h> |
15 | #include <linux/acpi.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/fs.h> |
18 | #include <linux/watchdog.h> |
19 | #include <linux/uaccess.h> |
20 | #include <linux/slab.h> |
21 | #include "sch56xx-common.h" |
22 | |
23 | /* Insmod parameters */ |
24 | static bool nowayout = WATCHDOG_NOWAYOUT; |
25 | module_param(nowayout, bool, 0); |
26 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
27 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")" ); |
28 | |
29 | #define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */ |
30 | #define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ |
31 | #define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ |
32 | |
33 | #define SIO_REG_LDSEL 0x07 /* Logical device select */ |
34 | #define SIO_REG_DEVID 0x20 /* Device ID */ |
35 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ |
36 | #define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ |
37 | |
38 | #define SIO_SCH5627_ID 0xC6 /* Chipset ID */ |
39 | #define SIO_SCH5636_ID 0xC7 /* Chipset ID */ |
40 | |
41 | #define REGION_LENGTH 10 |
42 | |
43 | #define SCH56XX_CMD_READ 0x02 |
44 | #define SCH56XX_CMD_WRITE 0x03 |
45 | |
46 | /* Watchdog registers */ |
47 | #define SCH56XX_REG_WDOG_PRESET 0x58B |
48 | #define SCH56XX_REG_WDOG_CONTROL 0x58C |
49 | #define SCH56XX_WDOG_TIME_BASE_SEC 0x01 |
50 | #define SCH56XX_REG_WDOG_OUTPUT_ENABLE 0x58E |
51 | #define SCH56XX_WDOG_OUTPUT_ENABLE 0x02 |
52 | |
53 | struct sch56xx_watchdog_data { |
54 | u16 addr; |
55 | struct mutex *io_lock; |
56 | struct watchdog_info wdinfo; |
57 | struct watchdog_device wddev; |
58 | u8 watchdog_preset; |
59 | u8 watchdog_control; |
60 | u8 watchdog_output_enable; |
61 | }; |
62 | |
63 | struct sch56xx_bus_context { |
64 | struct mutex *lock; /* Used to serialize access to the mailbox registers */ |
65 | u16 addr; |
66 | }; |
67 | |
68 | static struct platform_device *sch56xx_pdev; |
69 | |
70 | /* Super I/O functions */ |
71 | static inline int superio_inb(int base, int reg) |
72 | { |
73 | outb(value: reg, port: base); |
74 | return inb(port: base + 1); |
75 | } |
76 | |
77 | static inline int superio_enter(int base) |
78 | { |
79 | /* Don't step on other drivers' I/O space by accident */ |
80 | if (!request_muxed_region(base, 2, "sch56xx" )) { |
81 | pr_err("I/O address 0x%04x already in use\n" , base); |
82 | return -EBUSY; |
83 | } |
84 | |
85 | outb(SIO_UNLOCK_KEY, port: base); |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static inline void superio_select(int base, int ld) |
91 | { |
92 | outb(SIO_REG_LDSEL, port: base); |
93 | outb(value: ld, port: base + 1); |
94 | } |
95 | |
96 | static inline void superio_exit(int base) |
97 | { |
98 | outb(SIO_LOCK_KEY, port: base); |
99 | release_region(base, 2); |
100 | } |
101 | |
102 | static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) |
103 | { |
104 | u8 val; |
105 | int i; |
106 | /* |
107 | * According to SMSC for the commands we use the maximum time for |
108 | * the EM to respond is 15 ms, but testing shows in practice it |
109 | * responds within 15-32 reads, so we first busy poll, and if |
110 | * that fails sleep a bit and try again until we are way past |
111 | * the 15 ms maximum response time. |
112 | */ |
113 | const int max_busy_polls = 64; |
114 | const int max_lazy_polls = 32; |
115 | |
116 | /* (Optional) Write-Clear the EC to Host Mailbox Register */ |
117 | val = inb(port: addr + 1); |
118 | outb(value: val, port: addr + 1); |
119 | |
120 | /* Set Mailbox Address Pointer to first location in Region 1 */ |
121 | outb(value: 0x00, port: addr + 2); |
122 | outb(value: 0x80, port: addr + 3); |
123 | |
124 | /* Write Request Packet Header */ |
125 | outb(value: cmd, port: addr + 4); /* VREG Access Type read:0x02 write:0x03 */ |
126 | outb(value: 0x01, port: addr + 5); /* # of Entries: 1 Byte (8-bit) */ |
127 | outb(value: 0x04, port: addr + 2); /* Mailbox AP to first data entry loc. */ |
128 | |
129 | /* Write Value field */ |
130 | if (cmd == SCH56XX_CMD_WRITE) |
131 | outb(value: v, port: addr + 4); |
132 | |
133 | /* Write Address field */ |
134 | outb(value: reg & 0xff, port: addr + 6); |
135 | outb(value: reg >> 8, port: addr + 7); |
136 | |
137 | /* Execute the Random Access Command */ |
138 | outb(value: 0x01, port: addr); /* Write 01h to the Host-to-EC register */ |
139 | |
140 | /* EM Interface Polling "Algorithm" */ |
141 | for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { |
142 | if (i >= max_busy_polls) |
143 | usleep_range(min: 1000, max: 2000); |
144 | /* Read Interrupt source Register */ |
145 | val = inb(port: addr + 8); |
146 | /* Write Clear the interrupt source bits */ |
147 | if (val) |
148 | outb(value: val, port: addr + 8); |
149 | /* Command Completed ? */ |
150 | if (val & 0x01) |
151 | break; |
152 | } |
153 | if (i == max_busy_polls + max_lazy_polls) { |
154 | pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n" , |
155 | reg, 1); |
156 | return -EIO; |
157 | } |
158 | |
159 | /* |
160 | * According to SMSC we may need to retry this, but sofar I've always |
161 | * seen this succeed in 1 try. |
162 | */ |
163 | for (i = 0; i < max_busy_polls; i++) { |
164 | /* Read EC-to-Host Register */ |
165 | val = inb(port: addr + 1); |
166 | /* Command Completed ? */ |
167 | if (val == 0x01) |
168 | break; |
169 | |
170 | if (i == 0) |
171 | pr_warn("EC reports: 0x%02x reading virtual register 0x%04hx\n" , |
172 | (unsigned int)val, reg); |
173 | } |
174 | if (i == max_busy_polls) { |
175 | pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n" , |
176 | reg, 2); |
177 | return -EIO; |
178 | } |
179 | |
180 | /* |
181 | * According to the SMSC app note we should now do: |
182 | * |
183 | * Set Mailbox Address Pointer to first location in Region 1 * |
184 | * outb(0x00, addr + 2); |
185 | * outb(0x80, addr + 3); |
186 | * |
187 | * But if we do that things don't work, so let's not. |
188 | */ |
189 | |
190 | /* Read Value field */ |
191 | if (cmd == SCH56XX_CMD_READ) |
192 | return inb(port: addr + 4); |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | int sch56xx_read_virtual_reg(u16 addr, u16 reg) |
198 | { |
199 | return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, v: 0); |
200 | } |
201 | EXPORT_SYMBOL(sch56xx_read_virtual_reg); |
202 | |
203 | int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val) |
204 | { |
205 | return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, v: val); |
206 | } |
207 | EXPORT_SYMBOL(sch56xx_write_virtual_reg); |
208 | |
209 | int sch56xx_read_virtual_reg16(u16 addr, u16 reg) |
210 | { |
211 | int lsb, msb; |
212 | |
213 | /* Read LSB first, this will cause the matching MSB to be latched */ |
214 | lsb = sch56xx_read_virtual_reg(addr, reg); |
215 | if (lsb < 0) |
216 | return lsb; |
217 | |
218 | msb = sch56xx_read_virtual_reg(addr, reg + 1); |
219 | if (msb < 0) |
220 | return msb; |
221 | |
222 | return lsb | (msb << 8); |
223 | } |
224 | EXPORT_SYMBOL(sch56xx_read_virtual_reg16); |
225 | |
226 | int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, |
227 | int high_nibble) |
228 | { |
229 | int msb, lsn; |
230 | |
231 | /* Read MSB first, this will cause the matching LSN to be latched */ |
232 | msb = sch56xx_read_virtual_reg(addr, msb_reg); |
233 | if (msb < 0) |
234 | return msb; |
235 | |
236 | lsn = sch56xx_read_virtual_reg(addr, lsn_reg); |
237 | if (lsn < 0) |
238 | return lsn; |
239 | |
240 | if (high_nibble) |
241 | return (msb << 4) | (lsn >> 4); |
242 | else |
243 | return (msb << 4) | (lsn & 0x0f); |
244 | } |
245 | EXPORT_SYMBOL(sch56xx_read_virtual_reg12); |
246 | |
247 | /* |
248 | * Regmap support |
249 | */ |
250 | |
251 | int sch56xx_regmap_read16(struct regmap *map, unsigned int reg, unsigned int *val) |
252 | { |
253 | int lsb, msb, ret; |
254 | |
255 | /* See sch56xx_read_virtual_reg16() */ |
256 | ret = regmap_read(map, reg, val: &lsb); |
257 | if (ret < 0) |
258 | return ret; |
259 | |
260 | ret = regmap_read(map, reg: reg + 1, val: &msb); |
261 | if (ret < 0) |
262 | return ret; |
263 | |
264 | *val = lsb | (msb << 8); |
265 | |
266 | return 0; |
267 | } |
268 | EXPORT_SYMBOL(sch56xx_regmap_read16); |
269 | |
270 | int sch56xx_regmap_write16(struct regmap *map, unsigned int reg, unsigned int val) |
271 | { |
272 | int ret; |
273 | |
274 | ret = regmap_write(map, reg, val: val & 0xff); |
275 | if (ret < 0) |
276 | return ret; |
277 | |
278 | return regmap_write(map, reg: reg + 1, val: (val >> 8) & 0xff); |
279 | } |
280 | EXPORT_SYMBOL(sch56xx_regmap_write16); |
281 | |
282 | static int sch56xx_reg_write(void *context, unsigned int reg, unsigned int val) |
283 | { |
284 | struct sch56xx_bus_context *bus = context; |
285 | int ret; |
286 | |
287 | mutex_lock(bus->lock); |
288 | ret = sch56xx_write_virtual_reg(bus->addr, (u16)reg, (u8)val); |
289 | mutex_unlock(lock: bus->lock); |
290 | |
291 | return ret; |
292 | } |
293 | |
294 | static int sch56xx_reg_read(void *context, unsigned int reg, unsigned int *val) |
295 | { |
296 | struct sch56xx_bus_context *bus = context; |
297 | int ret; |
298 | |
299 | mutex_lock(bus->lock); |
300 | ret = sch56xx_read_virtual_reg(bus->addr, (u16)reg); |
301 | mutex_unlock(lock: bus->lock); |
302 | |
303 | if (ret < 0) |
304 | return ret; |
305 | |
306 | *val = ret; |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | static void sch56xx_free_context(void *context) |
312 | { |
313 | kfree(objp: context); |
314 | } |
315 | |
316 | static const struct regmap_bus sch56xx_bus = { |
317 | .reg_write = sch56xx_reg_write, |
318 | .reg_read = sch56xx_reg_read, |
319 | .free_context = sch56xx_free_context, |
320 | .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, |
321 | .val_format_endian_default = REGMAP_ENDIAN_LITTLE, |
322 | }; |
323 | |
324 | struct regmap *devm_regmap_init_sch56xx(struct device *dev, struct mutex *lock, u16 addr, |
325 | const struct regmap_config *config) |
326 | { |
327 | struct sch56xx_bus_context *context; |
328 | struct regmap *map; |
329 | |
330 | if (config->reg_bits != 16 && config->val_bits != 8) |
331 | return ERR_PTR(error: -EOPNOTSUPP); |
332 | |
333 | context = kzalloc(size: sizeof(*context), GFP_KERNEL); |
334 | if (!context) |
335 | return ERR_PTR(error: -ENOMEM); |
336 | |
337 | context->lock = lock; |
338 | context->addr = addr; |
339 | |
340 | map = devm_regmap_init(dev, &sch56xx_bus, context, config); |
341 | if (IS_ERR(ptr: map)) |
342 | kfree(objp: context); |
343 | |
344 | return map; |
345 | } |
346 | EXPORT_SYMBOL(devm_regmap_init_sch56xx); |
347 | |
348 | /* |
349 | * Watchdog routines |
350 | */ |
351 | |
352 | static int watchdog_set_timeout(struct watchdog_device *wddev, |
353 | unsigned int timeout) |
354 | { |
355 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wdd: wddev); |
356 | unsigned int resolution; |
357 | u8 control; |
358 | int ret; |
359 | |
360 | /* 1 second or 60 second resolution? */ |
361 | if (timeout <= 255) |
362 | resolution = 1; |
363 | else |
364 | resolution = 60; |
365 | |
366 | if (timeout < resolution || timeout > (resolution * 255)) |
367 | return -EINVAL; |
368 | |
369 | if (resolution == 1) |
370 | control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC; |
371 | else |
372 | control = data->watchdog_control & ~SCH56XX_WDOG_TIME_BASE_SEC; |
373 | |
374 | if (data->watchdog_control != control) { |
375 | mutex_lock(data->io_lock); |
376 | ret = sch56xx_write_virtual_reg(data->addr, |
377 | SCH56XX_REG_WDOG_CONTROL, |
378 | control); |
379 | mutex_unlock(lock: data->io_lock); |
380 | if (ret) |
381 | return ret; |
382 | |
383 | data->watchdog_control = control; |
384 | } |
385 | |
386 | /* |
387 | * Remember new timeout value, but do not write as that (re)starts |
388 | * the watchdog countdown. |
389 | */ |
390 | data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); |
391 | wddev->timeout = data->watchdog_preset * resolution; |
392 | |
393 | return 0; |
394 | } |
395 | |
396 | static int watchdog_start(struct watchdog_device *wddev) |
397 | { |
398 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wdd: wddev); |
399 | int ret; |
400 | u8 val; |
401 | |
402 | /* |
403 | * The sch56xx's watchdog cannot really be started / stopped |
404 | * it is always running, but we can avoid the timer expiring |
405 | * from causing a system reset by clearing the output enable bit. |
406 | * |
407 | * The sch56xx's watchdog will set the watchdog event bit, bit 0 |
408 | * of the second interrupt source register (at base-address + 9), |
409 | * when the timer expires. |
410 | * |
411 | * This will only cause a system reset if the 0-1 flank happens when |
412 | * output enable is true. Setting output enable after the flank will |
413 | * not cause a reset, nor will the timer expiring a second time. |
414 | * This means we must clear the watchdog event bit in case it is set. |
415 | * |
416 | * The timer may still be running (after a recent watchdog_stop) and |
417 | * mere milliseconds away from expiring, so the timer must be reset |
418 | * first! |
419 | */ |
420 | |
421 | mutex_lock(data->io_lock); |
422 | |
423 | /* 1. Reset the watchdog countdown counter */ |
424 | ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, |
425 | data->watchdog_preset); |
426 | if (ret) |
427 | goto leave; |
428 | |
429 | /* 2. Enable output */ |
430 | val = data->watchdog_output_enable | SCH56XX_WDOG_OUTPUT_ENABLE; |
431 | ret = sch56xx_write_virtual_reg(data->addr, |
432 | SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); |
433 | if (ret) |
434 | goto leave; |
435 | |
436 | data->watchdog_output_enable = val; |
437 | |
438 | /* 3. Clear the watchdog event bit if set */ |
439 | val = inb(port: data->addr + 9); |
440 | if (val & 0x01) |
441 | outb(value: 0x01, port: data->addr + 9); |
442 | |
443 | leave: |
444 | mutex_unlock(lock: data->io_lock); |
445 | return ret; |
446 | } |
447 | |
448 | static int watchdog_trigger(struct watchdog_device *wddev) |
449 | { |
450 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wdd: wddev); |
451 | int ret; |
452 | |
453 | /* Reset the watchdog countdown counter */ |
454 | mutex_lock(data->io_lock); |
455 | ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, |
456 | data->watchdog_preset); |
457 | mutex_unlock(lock: data->io_lock); |
458 | |
459 | return ret; |
460 | } |
461 | |
462 | static int watchdog_stop(struct watchdog_device *wddev) |
463 | { |
464 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wdd: wddev); |
465 | int ret = 0; |
466 | u8 val; |
467 | |
468 | val = data->watchdog_output_enable & ~SCH56XX_WDOG_OUTPUT_ENABLE; |
469 | mutex_lock(data->io_lock); |
470 | ret = sch56xx_write_virtual_reg(data->addr, |
471 | SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); |
472 | mutex_unlock(lock: data->io_lock); |
473 | if (ret) |
474 | return ret; |
475 | |
476 | data->watchdog_output_enable = val; |
477 | return 0; |
478 | } |
479 | |
480 | static const struct watchdog_ops watchdog_ops = { |
481 | .owner = THIS_MODULE, |
482 | .start = watchdog_start, |
483 | .stop = watchdog_stop, |
484 | .ping = watchdog_trigger, |
485 | .set_timeout = watchdog_set_timeout, |
486 | }; |
487 | |
488 | void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, |
489 | struct mutex *io_lock, int check_enabled) |
490 | { |
491 | struct sch56xx_watchdog_data *data; |
492 | int err, control, output_enable; |
493 | |
494 | /* Cache the watchdog registers */ |
495 | mutex_lock(io_lock); |
496 | control = |
497 | sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_CONTROL); |
498 | output_enable = |
499 | sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_OUTPUT_ENABLE); |
500 | mutex_unlock(lock: io_lock); |
501 | |
502 | if (control < 0) |
503 | return; |
504 | if (output_enable < 0) |
505 | return; |
506 | if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) { |
507 | pr_warn("Watchdog not enabled by BIOS, not registering\n" ); |
508 | return; |
509 | } |
510 | |
511 | data = devm_kzalloc(dev: parent, size: sizeof(struct sch56xx_watchdog_data), GFP_KERNEL); |
512 | if (!data) |
513 | return; |
514 | |
515 | data->addr = addr; |
516 | data->io_lock = io_lock; |
517 | |
518 | strscpy(p: data->wdinfo.identity, q: "sch56xx watchdog" , size: sizeof(data->wdinfo.identity)); |
519 | data->wdinfo.firmware_version = revision; |
520 | data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT; |
521 | if (!nowayout) |
522 | data->wdinfo.options |= WDIOF_MAGICCLOSE; |
523 | |
524 | data->wddev.info = &data->wdinfo; |
525 | data->wddev.ops = &watchdog_ops; |
526 | data->wddev.parent = parent; |
527 | data->wddev.timeout = 60; |
528 | data->wddev.min_timeout = 1; |
529 | data->wddev.max_timeout = 255 * 60; |
530 | watchdog_set_nowayout(wdd: &data->wddev, nowayout); |
531 | if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) |
532 | set_bit(WDOG_HW_RUNNING, addr: &data->wddev.status); |
533 | |
534 | /* Since the watchdog uses a downcounter there is no register to read |
535 | the BIOS set timeout from (if any was set at all) -> |
536 | Choose a preset which will give us a 1 minute timeout */ |
537 | if (control & SCH56XX_WDOG_TIME_BASE_SEC) |
538 | data->watchdog_preset = 60; /* seconds */ |
539 | else |
540 | data->watchdog_preset = 1; /* minute */ |
541 | |
542 | data->watchdog_control = control; |
543 | data->watchdog_output_enable = output_enable; |
544 | |
545 | watchdog_set_drvdata(wdd: &data->wddev, data); |
546 | err = devm_watchdog_register_device(dev: parent, &data->wddev); |
547 | if (err) { |
548 | pr_err("Registering watchdog chardev: %d\n" , err); |
549 | devm_kfree(dev: parent, p: data); |
550 | } |
551 | } |
552 | EXPORT_SYMBOL(sch56xx_watchdog_register); |
553 | |
554 | /* |
555 | * platform dev find, add and remove functions |
556 | */ |
557 | |
558 | static int __init sch56xx_find(int sioaddr, const char **name) |
559 | { |
560 | u8 devid; |
561 | unsigned short address; |
562 | int err; |
563 | |
564 | err = superio_enter(base: sioaddr); |
565 | if (err) |
566 | return err; |
567 | |
568 | devid = superio_inb(base: sioaddr, SIO_REG_DEVID); |
569 | switch (devid) { |
570 | case SIO_SCH5627_ID: |
571 | *name = "sch5627" ; |
572 | break; |
573 | case SIO_SCH5636_ID: |
574 | *name = "sch5636" ; |
575 | break; |
576 | default: |
577 | pr_debug("Unsupported device id: 0x%02x\n" , |
578 | (unsigned int)devid); |
579 | err = -ENODEV; |
580 | goto exit; |
581 | } |
582 | |
583 | superio_select(base: sioaddr, SIO_SCH56XX_LD_EM); |
584 | |
585 | if (!(superio_inb(base: sioaddr, SIO_REG_ENABLE) & 0x01)) { |
586 | pr_warn("Device not activated\n" ); |
587 | err = -ENODEV; |
588 | goto exit; |
589 | } |
590 | |
591 | /* |
592 | * Warning the order of the low / high byte is the other way around |
593 | * as on most other superio devices!! |
594 | */ |
595 | address = superio_inb(base: sioaddr, SIO_REG_ADDR) | |
596 | superio_inb(base: sioaddr, SIO_REG_ADDR + 1) << 8; |
597 | if (address == 0) { |
598 | pr_warn("Base address not set\n" ); |
599 | err = -ENODEV; |
600 | goto exit; |
601 | } |
602 | err = address; |
603 | |
604 | exit: |
605 | superio_exit(base: sioaddr); |
606 | return err; |
607 | } |
608 | |
609 | static int __init sch56xx_device_add(int address, const char *name) |
610 | { |
611 | struct resource res = { |
612 | .start = address, |
613 | .end = address + REGION_LENGTH - 1, |
614 | .name = name, |
615 | .flags = IORESOURCE_IO, |
616 | }; |
617 | int err; |
618 | |
619 | err = acpi_check_resource_conflict(res: &res); |
620 | if (err) |
621 | return err; |
622 | |
623 | sch56xx_pdev = platform_device_register_simple(name, id: -1, res: &res, num: 1); |
624 | |
625 | return PTR_ERR_OR_ZERO(ptr: sch56xx_pdev); |
626 | } |
627 | |
628 | static int __init sch56xx_init(void) |
629 | { |
630 | int address; |
631 | const char *name = NULL; |
632 | |
633 | address = sch56xx_find(sioaddr: 0x4e, name: &name); |
634 | if (address < 0) |
635 | address = sch56xx_find(sioaddr: 0x2e, name: &name); |
636 | if (address < 0) |
637 | return address; |
638 | |
639 | return sch56xx_device_add(address, name); |
640 | } |
641 | |
642 | static void __exit sch56xx_exit(void) |
643 | { |
644 | platform_device_unregister(sch56xx_pdev); |
645 | } |
646 | |
647 | MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code" ); |
648 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
649 | MODULE_LICENSE("GPL" ); |
650 | |
651 | module_init(sch56xx_init); |
652 | module_exit(sch56xx_exit); |
653 | |