Commit 28ff2f7a742daba86ccd7021be7b27a4673b2797
Committed by
Jean Delvare
1 parent
b6844e8f64
Exists in
master
and in
38 other branches
hwmon: (sch5627) Factor out some code shared with sch5636 driver
This patch adds a new sch56xx-common.ko which contains code which will also be used in the new sch5636 driver. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Showing 5 changed files with 390 additions and 310 deletions Side-by-side Diff
drivers/hwmon/Kconfig
... | ... | @@ -1041,8 +1041,13 @@ |
1041 | 1041 | This driver can also be built as a module. If so, the module |
1042 | 1042 | will be called smsc47b397. |
1043 | 1043 | |
1044 | +config SENSORS_SCH56XX_COMMON | |
1045 | + tristate | |
1046 | + default n | |
1047 | + | |
1044 | 1048 | config SENSORS_SCH5627 |
1045 | 1049 | tristate "SMSC SCH5627" |
1050 | + select SENSORS_SCH56XX_COMMON | |
1046 | 1051 | help |
1047 | 1052 | If you say yes here you get support for the hardware monitoring |
1048 | 1053 | features of the SMSC SCH5627 Super-I/O chip. |
drivers/hwmon/Makefile
... | ... | @@ -95,6 +95,7 @@ |
95 | 95 | obj-$(CONFIG_SENSORS_PC87427) += pc87427.o |
96 | 96 | obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o |
97 | 97 | obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o |
98 | +obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o | |
98 | 99 | obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o |
99 | 100 | obj-$(CONFIG_SENSORS_SHT15) += sht15.o |
100 | 101 | obj-$(CONFIG_SENSORS_SHT21) += sht21.o |
drivers/hwmon/sch5627.c
... | ... | @@ -28,33 +28,15 @@ |
28 | 28 | #include <linux/hwmon-sysfs.h> |
29 | 29 | #include <linux/err.h> |
30 | 30 | #include <linux/mutex.h> |
31 | -#include <linux/io.h> | |
32 | -#include <linux/acpi.h> | |
33 | -#include <linux/delay.h> | |
31 | +#include "sch56xx-common.h" | |
34 | 32 | |
35 | 33 | #define DRVNAME "sch5627" |
36 | 34 | #define DEVNAME DRVNAME /* We only support one model */ |
37 | 35 | |
38 | -#define SIO_SCH5627_EM_LD 0x0C /* Embedded Microcontroller LD */ | |
39 | -#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ | |
40 | -#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ | |
41 | - | |
42 | -#define SIO_REG_LDSEL 0x07 /* Logical device select */ | |
43 | -#define SIO_REG_DEVID 0x20 /* Device ID */ | |
44 | -#define SIO_REG_ENABLE 0x30 /* Logical device enable */ | |
45 | -#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ | |
46 | - | |
47 | -#define SIO_SCH5627_ID 0xC6 /* Chipset ID */ | |
48 | - | |
49 | -#define REGION_LENGTH 9 | |
50 | - | |
51 | 36 | #define SCH5627_HWMON_ID 0xa5 |
52 | 37 | #define SCH5627_COMPANY_ID 0x5c |
53 | 38 | #define SCH5627_PRIMARY_ID 0xa0 |
54 | 39 | |
55 | -#define SCH5627_CMD_READ 0x02 | |
56 | -#define SCH5627_CMD_WRITE 0x03 | |
57 | - | |
58 | 40 | #define SCH5627_REG_BUILD_CODE 0x39 |
59 | 41 | #define SCH5627_REG_BUILD_ID 0x3a |
60 | 42 | #define SCH5627_REG_HWMON_ID 0x3c |
... | ... | @@ -111,182 +93,6 @@ |
111 | 93 | u16 in[SCH5627_NO_IN]; |
112 | 94 | }; |
113 | 95 | |
114 | -static struct platform_device *sch5627_pdev; | |
115 | - | |
116 | -/* Super I/O functions */ | |
117 | -static inline int superio_inb(int base, int reg) | |
118 | -{ | |
119 | - outb(reg, base); | |
120 | - return inb(base + 1); | |
121 | -} | |
122 | - | |
123 | -static inline int superio_enter(int base) | |
124 | -{ | |
125 | - /* Don't step on other drivers' I/O space by accident */ | |
126 | - if (!request_muxed_region(base, 2, DRVNAME)) { | |
127 | - pr_err("I/O address 0x%04x already in use\n", base); | |
128 | - return -EBUSY; | |
129 | - } | |
130 | - | |
131 | - outb(SIO_UNLOCK_KEY, base); | |
132 | - | |
133 | - return 0; | |
134 | -} | |
135 | - | |
136 | -static inline void superio_select(int base, int ld) | |
137 | -{ | |
138 | - outb(SIO_REG_LDSEL, base); | |
139 | - outb(ld, base + 1); | |
140 | -} | |
141 | - | |
142 | -static inline void superio_exit(int base) | |
143 | -{ | |
144 | - outb(SIO_LOCK_KEY, base); | |
145 | - release_region(base, 2); | |
146 | -} | |
147 | - | |
148 | -static int sch5627_send_cmd(struct sch5627_data *data, u8 cmd, u16 reg, u8 v) | |
149 | -{ | |
150 | - u8 val; | |
151 | - int i; | |
152 | - /* | |
153 | - * According to SMSC for the commands we use the maximum time for | |
154 | - * the EM to respond is 15 ms, but testing shows in practice it | |
155 | - * responds within 15-32 reads, so we first busy poll, and if | |
156 | - * that fails sleep a bit and try again until we are way past | |
157 | - * the 15 ms maximum response time. | |
158 | - */ | |
159 | - const int max_busy_polls = 64; | |
160 | - const int max_lazy_polls = 32; | |
161 | - | |
162 | - /* (Optional) Write-Clear the EC to Host Mailbox Register */ | |
163 | - val = inb(data->addr + 1); | |
164 | - outb(val, data->addr + 1); | |
165 | - | |
166 | - /* Set Mailbox Address Pointer to first location in Region 1 */ | |
167 | - outb(0x00, data->addr + 2); | |
168 | - outb(0x80, data->addr + 3); | |
169 | - | |
170 | - /* Write Request Packet Header */ | |
171 | - outb(cmd, data->addr + 4); /* VREG Access Type read:0x02 write:0x03 */ | |
172 | - outb(0x01, data->addr + 5); /* # of Entries: 1 Byte (8-bit) */ | |
173 | - outb(0x04, data->addr + 2); /* Mailbox AP to first data entry loc. */ | |
174 | - | |
175 | - /* Write Value field */ | |
176 | - if (cmd == SCH5627_CMD_WRITE) | |
177 | - outb(v, data->addr + 4); | |
178 | - | |
179 | - /* Write Address field */ | |
180 | - outb(reg & 0xff, data->addr + 6); | |
181 | - outb(reg >> 8, data->addr + 7); | |
182 | - | |
183 | - /* Execute the Random Access Command */ | |
184 | - outb(0x01, data->addr); /* Write 01h to the Host-to-EC register */ | |
185 | - | |
186 | - /* EM Interface Polling "Algorithm" */ | |
187 | - for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { | |
188 | - if (i >= max_busy_polls) | |
189 | - msleep(1); | |
190 | - /* Read Interrupt source Register */ | |
191 | - val = inb(data->addr + 8); | |
192 | - /* Write Clear the interrupt source bits */ | |
193 | - if (val) | |
194 | - outb(val, data->addr + 8); | |
195 | - /* Command Completed ? */ | |
196 | - if (val & 0x01) | |
197 | - break; | |
198 | - } | |
199 | - if (i == max_busy_polls + max_lazy_polls) { | |
200 | - pr_err("Max retries exceeded reading virtual " | |
201 | - "register 0x%04hx (%d)\n", reg, 1); | |
202 | - return -EIO; | |
203 | - } | |
204 | - | |
205 | - /* | |
206 | - * According to SMSC we may need to retry this, but sofar I've always | |
207 | - * seen this succeed in 1 try. | |
208 | - */ | |
209 | - for (i = 0; i < max_busy_polls; i++) { | |
210 | - /* Read EC-to-Host Register */ | |
211 | - val = inb(data->addr + 1); | |
212 | - /* Command Completed ? */ | |
213 | - if (val == 0x01) | |
214 | - break; | |
215 | - | |
216 | - if (i == 0) | |
217 | - pr_warn("EC reports: 0x%02x reading virtual register " | |
218 | - "0x%04hx\n", (unsigned int)val, reg); | |
219 | - } | |
220 | - if (i == max_busy_polls) { | |
221 | - pr_err("Max retries exceeded reading virtual " | |
222 | - "register 0x%04hx (%d)\n", reg, 2); | |
223 | - return -EIO; | |
224 | - } | |
225 | - | |
226 | - /* | |
227 | - * According to the SMSC app note we should now do: | |
228 | - * | |
229 | - * Set Mailbox Address Pointer to first location in Region 1 * | |
230 | - * outb(0x00, data->addr + 2); | |
231 | - * outb(0x80, data->addr + 3); | |
232 | - * | |
233 | - * But if we do that things don't work, so let's not. | |
234 | - */ | |
235 | - | |
236 | - /* Read Value field */ | |
237 | - if (cmd == SCH5627_CMD_READ) | |
238 | - return inb(data->addr + 4); | |
239 | - | |
240 | - return 0; | |
241 | -} | |
242 | - | |
243 | -static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg) | |
244 | -{ | |
245 | - return sch5627_send_cmd(data, SCH5627_CMD_READ, reg, 0); | |
246 | -} | |
247 | - | |
248 | -static int sch5627_write_virtual_reg(struct sch5627_data *data, | |
249 | - u16 reg, u8 val) | |
250 | -{ | |
251 | - return sch5627_send_cmd(data, SCH5627_CMD_WRITE, reg, val); | |
252 | -} | |
253 | - | |
254 | -static int sch5627_read_virtual_reg16(struct sch5627_data *data, u16 reg) | |
255 | -{ | |
256 | - int lsb, msb; | |
257 | - | |
258 | - /* Read LSB first, this will cause the matching MSB to be latched */ | |
259 | - lsb = sch5627_read_virtual_reg(data, reg); | |
260 | - if (lsb < 0) | |
261 | - return lsb; | |
262 | - | |
263 | - msb = sch5627_read_virtual_reg(data, reg + 1); | |
264 | - if (msb < 0) | |
265 | - return msb; | |
266 | - | |
267 | - return lsb | (msb << 8); | |
268 | -} | |
269 | - | |
270 | -static int sch5627_read_virtual_reg12(struct sch5627_data *data, u16 msb_reg, | |
271 | - u16 lsn_reg, int high_nibble) | |
272 | -{ | |
273 | - int msb, lsn; | |
274 | - | |
275 | - /* Read MSB first, this will cause the matching LSN to be latched */ | |
276 | - msb = sch5627_read_virtual_reg(data, msb_reg); | |
277 | - if (msb < 0) | |
278 | - return msb; | |
279 | - | |
280 | - lsn = sch5627_read_virtual_reg(data, lsn_reg); | |
281 | - if (lsn < 0) | |
282 | - return lsn; | |
283 | - | |
284 | - if (high_nibble) | |
285 | - return (msb << 4) | (lsn >> 4); | |
286 | - else | |
287 | - return (msb << 4) | (lsn & 0x0f); | |
288 | -} | |
289 | - | |
290 | 96 | static struct sch5627_data *sch5627_update_device(struct device *dev) |
291 | 97 | { |
292 | 98 | struct sch5627_data *data = dev_get_drvdata(dev); |
... | ... | @@ -297,7 +103,7 @@ |
297 | 103 | |
298 | 104 | /* Trigger a Vbat voltage measurement every 5 minutes */ |
299 | 105 | if (time_after(jiffies, data->last_battery + 300 * HZ)) { |
300 | - sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, | |
106 | + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, | |
301 | 107 | data->control | 0x10); |
302 | 108 | data->last_battery = jiffies; |
303 | 109 | } |
... | ... | @@ -305,7 +111,7 @@ |
305 | 111 | /* Cache the values for 1 second */ |
306 | 112 | if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { |
307 | 113 | for (i = 0; i < SCH5627_NO_TEMPS; i++) { |
308 | - val = sch5627_read_virtual_reg12(data, | |
114 | + val = sch56xx_read_virtual_reg12(data->addr, | |
309 | 115 | SCH5627_REG_TEMP_MSB[i], |
310 | 116 | SCH5627_REG_TEMP_LSN[i], |
311 | 117 | SCH5627_REG_TEMP_HIGH_NIBBLE[i]); |
... | ... | @@ -317,7 +123,7 @@ |
317 | 123 | } |
318 | 124 | |
319 | 125 | for (i = 0; i < SCH5627_NO_FANS; i++) { |
320 | - val = sch5627_read_virtual_reg16(data, | |
126 | + val = sch56xx_read_virtual_reg16(data->addr, | |
321 | 127 | SCH5627_REG_FAN[i]); |
322 | 128 | if (unlikely(val < 0)) { |
323 | 129 | ret = ERR_PTR(val); |
... | ... | @@ -327,7 +133,7 @@ |
327 | 133 | } |
328 | 134 | |
329 | 135 | for (i = 0; i < SCH5627_NO_IN; i++) { |
330 | - val = sch5627_read_virtual_reg12(data, | |
136 | + val = sch56xx_read_virtual_reg12(data->addr, | |
331 | 137 | SCH5627_REG_IN_MSB[i], |
332 | 138 | SCH5627_REG_IN_LSN[i], |
333 | 139 | SCH5627_REG_IN_HIGH_NIBBLE[i]); |
334 | 140 | |
335 | 141 | |
... | ... | @@ -355,18 +161,21 @@ |
355 | 161 | * Note what SMSC calls ABS, is what lm_sensors calls max |
356 | 162 | * (aka high), and HIGH is what lm_sensors calls crit. |
357 | 163 | */ |
358 | - val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_ABS[i]); | |
164 | + val = sch56xx_read_virtual_reg(data->addr, | |
165 | + SCH5627_REG_TEMP_ABS[i]); | |
359 | 166 | if (val < 0) |
360 | 167 | return val; |
361 | 168 | data->temp_max[i] = val; |
362 | 169 | |
363 | - val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_HIGH[i]); | |
170 | + val = sch56xx_read_virtual_reg(data->addr, | |
171 | + SCH5627_REG_TEMP_HIGH[i]); | |
364 | 172 | if (val < 0) |
365 | 173 | return val; |
366 | 174 | data->temp_crit[i] = val; |
367 | 175 | } |
368 | 176 | for (i = 0; i < SCH5627_NO_FANS; i++) { |
369 | - val = sch5627_read_virtual_reg16(data, SCH5627_REG_FAN_MIN[i]); | |
177 | + val = sch56xx_read_virtual_reg16(data->addr, | |
178 | + SCH5627_REG_FAN_MIN[i]); | |
370 | 179 | if (val < 0) |
371 | 180 | return val; |
372 | 181 | data->fan_min[i] = val; |
... | ... | @@ -667,7 +476,7 @@ |
667 | 476 | mutex_init(&data->update_lock); |
668 | 477 | platform_set_drvdata(pdev, data); |
669 | 478 | |
670 | - val = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_ID); | |
479 | + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID); | |
671 | 480 | if (val < 0) { |
672 | 481 | err = val; |
673 | 482 | goto error; |
... | ... | @@ -679,7 +488,7 @@ |
679 | 488 | goto error; |
680 | 489 | } |
681 | 490 | |
682 | - val = sch5627_read_virtual_reg(data, SCH5627_REG_COMPANY_ID); | |
491 | + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID); | |
683 | 492 | if (val < 0) { |
684 | 493 | err = val; |
685 | 494 | goto error; |
... | ... | @@ -691,7 +500,7 @@ |
691 | 500 | goto error; |
692 | 501 | } |
693 | 502 | |
694 | - val = sch5627_read_virtual_reg(data, SCH5627_REG_PRIMARY_ID); | |
503 | + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID); | |
695 | 504 | if (val < 0) { |
696 | 505 | err = val; |
697 | 506 | goto error; |
698 | 507 | |
699 | 508 | |
700 | 509 | |
... | ... | @@ -703,25 +512,28 @@ |
703 | 512 | goto error; |
704 | 513 | } |
705 | 514 | |
706 | - build_code = sch5627_read_virtual_reg(data, SCH5627_REG_BUILD_CODE); | |
515 | + build_code = sch56xx_read_virtual_reg(data->addr, | |
516 | + SCH5627_REG_BUILD_CODE); | |
707 | 517 | if (build_code < 0) { |
708 | 518 | err = build_code; |
709 | 519 | goto error; |
710 | 520 | } |
711 | 521 | |
712 | - build_id = sch5627_read_virtual_reg16(data, SCH5627_REG_BUILD_ID); | |
522 | + build_id = sch56xx_read_virtual_reg16(data->addr, | |
523 | + SCH5627_REG_BUILD_ID); | |
713 | 524 | if (build_id < 0) { |
714 | 525 | err = build_id; |
715 | 526 | goto error; |
716 | 527 | } |
717 | 528 | |
718 | - hwmon_rev = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_REV); | |
529 | + hwmon_rev = sch56xx_read_virtual_reg(data->addr, | |
530 | + SCH5627_REG_HWMON_REV); | |
719 | 531 | if (hwmon_rev < 0) { |
720 | 532 | err = hwmon_rev; |
721 | 533 | goto error; |
722 | 534 | } |
723 | 535 | |
724 | - val = sch5627_read_virtual_reg(data, SCH5627_REG_CTRL); | |
536 | + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL); | |
725 | 537 | if (val < 0) { |
726 | 538 | err = val; |
727 | 539 | goto error; |
... | ... | @@ -734,7 +546,7 @@ |
734 | 546 | } |
735 | 547 | /* Trigger a Vbat voltage measurement, so that we get a valid reading |
736 | 548 | the first time we read Vbat */ |
737 | - sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, | |
549 | + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, | |
738 | 550 | data->control | 0x10); |
739 | 551 | data->last_battery = jiffies; |
740 | 552 | |
... | ... | @@ -746,6 +558,7 @@ |
746 | 558 | if (err) |
747 | 559 | goto error; |
748 | 560 | |
561 | + pr_info("found %s chip at %#hx\n", DEVNAME, data->addr); | |
749 | 562 | pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", |
750 | 563 | build_code, build_id, hwmon_rev); |
751 | 564 | |
... | ... | @@ -768,85 +581,6 @@ |
768 | 581 | return err; |
769 | 582 | } |
770 | 583 | |
771 | -static int __init sch5627_find(int sioaddr, unsigned short *address) | |
772 | -{ | |
773 | - u8 devid; | |
774 | - int err = superio_enter(sioaddr); | |
775 | - if (err) | |
776 | - return err; | |
777 | - | |
778 | - devid = superio_inb(sioaddr, SIO_REG_DEVID); | |
779 | - if (devid != SIO_SCH5627_ID) { | |
780 | - pr_debug("Unsupported device id: 0x%02x\n", | |
781 | - (unsigned int)devid); | |
782 | - err = -ENODEV; | |
783 | - goto exit; | |
784 | - } | |
785 | - | |
786 | - superio_select(sioaddr, SIO_SCH5627_EM_LD); | |
787 | - | |
788 | - if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { | |
789 | - pr_warn("Device not activated\n"); | |
790 | - err = -ENODEV; | |
791 | - goto exit; | |
792 | - } | |
793 | - | |
794 | - /* | |
795 | - * Warning the order of the low / high byte is the other way around | |
796 | - * as on most other superio devices!! | |
797 | - */ | |
798 | - *address = superio_inb(sioaddr, SIO_REG_ADDR) | | |
799 | - superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; | |
800 | - if (*address == 0) { | |
801 | - pr_warn("Base address not set\n"); | |
802 | - err = -ENODEV; | |
803 | - goto exit; | |
804 | - } | |
805 | - | |
806 | - pr_info("Found %s chip at %#hx\n", DEVNAME, *address); | |
807 | -exit: | |
808 | - superio_exit(sioaddr); | |
809 | - return err; | |
810 | -} | |
811 | - | |
812 | -static int __init sch5627_device_add(unsigned short address) | |
813 | -{ | |
814 | - struct resource res = { | |
815 | - .start = address, | |
816 | - .end = address + REGION_LENGTH - 1, | |
817 | - .flags = IORESOURCE_IO, | |
818 | - }; | |
819 | - int err; | |
820 | - | |
821 | - sch5627_pdev = platform_device_alloc(DRVNAME, address); | |
822 | - if (!sch5627_pdev) | |
823 | - return -ENOMEM; | |
824 | - | |
825 | - res.name = sch5627_pdev->name; | |
826 | - err = acpi_check_resource_conflict(&res); | |
827 | - if (err) | |
828 | - goto exit_device_put; | |
829 | - | |
830 | - err = platform_device_add_resources(sch5627_pdev, &res, 1); | |
831 | - if (err) { | |
832 | - pr_err("Device resource addition failed\n"); | |
833 | - goto exit_device_put; | |
834 | - } | |
835 | - | |
836 | - err = platform_device_add(sch5627_pdev); | |
837 | - if (err) { | |
838 | - pr_err("Device addition failed\n"); | |
839 | - goto exit_device_put; | |
840 | - } | |
841 | - | |
842 | - return 0; | |
843 | - | |
844 | -exit_device_put: | |
845 | - platform_device_put(sch5627_pdev); | |
846 | - | |
847 | - return err; | |
848 | -} | |
849 | - | |
850 | 584 | static struct platform_driver sch5627_driver = { |
851 | 585 | .driver = { |
852 | 586 | .owner = THIS_MODULE, |
853 | 587 | |
... | ... | @@ -858,31 +592,11 @@ |
858 | 592 | |
859 | 593 | static int __init sch5627_init(void) |
860 | 594 | { |
861 | - int err = -ENODEV; | |
862 | - unsigned short address; | |
863 | - | |
864 | - if (sch5627_find(0x4e, &address) && sch5627_find(0x2e, &address)) | |
865 | - goto exit; | |
866 | - | |
867 | - err = platform_driver_register(&sch5627_driver); | |
868 | - if (err) | |
869 | - goto exit; | |
870 | - | |
871 | - err = sch5627_device_add(address); | |
872 | - if (err) | |
873 | - goto exit_driver; | |
874 | - | |
875 | - return 0; | |
876 | - | |
877 | -exit_driver: | |
878 | - platform_driver_unregister(&sch5627_driver); | |
879 | -exit: | |
880 | - return err; | |
595 | + return platform_driver_register(&sch5627_driver); | |
881 | 596 | } |
882 | 597 | |
883 | 598 | static void __exit sch5627_exit(void) |
884 | 599 | { |
885 | - platform_device_unregister(sch5627_pdev); | |
886 | 600 | platform_driver_unregister(&sch5627_driver); |
887 | 601 | } |
888 | 602 |
drivers/hwmon/sch56xx-common.c
1 | +/*************************************************************************** | |
2 | + * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> * | |
3 | + * * | |
4 | + * This program is free software; you can redistribute it and/or modify * | |
5 | + * it under the terms of the GNU General Public License as published by * | |
6 | + * the Free Software Foundation; either version 2 of the License, or * | |
7 | + * (at your option) any later version. * | |
8 | + * * | |
9 | + * This program is distributed in the hope that it will be useful, * | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
12 | + * GNU General Public License for more details. * | |
13 | + * * | |
14 | + * You should have received a copy of the GNU General Public License * | |
15 | + * along with this program; if not, write to the * | |
16 | + * Free Software Foundation, Inc., * | |
17 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
18 | + ***************************************************************************/ | |
19 | + | |
20 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
21 | + | |
22 | +#include <linux/module.h> | |
23 | +#include <linux/init.h> | |
24 | +#include <linux/platform_device.h> | |
25 | +#include <linux/err.h> | |
26 | +#include <linux/io.h> | |
27 | +#include <linux/acpi.h> | |
28 | +#include <linux/delay.h> | |
29 | +#include "sch56xx-common.h" | |
30 | + | |
31 | +#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */ | |
32 | +#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ | |
33 | +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ | |
34 | + | |
35 | +#define SIO_REG_LDSEL 0x07 /* Logical device select */ | |
36 | +#define SIO_REG_DEVID 0x20 /* Device ID */ | |
37 | +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ | |
38 | +#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ | |
39 | + | |
40 | +#define SIO_SCH5627_ID 0xC6 /* Chipset ID */ | |
41 | + | |
42 | +#define REGION_LENGTH 9 | |
43 | + | |
44 | +#define SCH56XX_CMD_READ 0x02 | |
45 | +#define SCH56XX_CMD_WRITE 0x03 | |
46 | + | |
47 | +static struct platform_device *sch56xx_pdev; | |
48 | + | |
49 | +/* Super I/O functions */ | |
50 | +static inline int superio_inb(int base, int reg) | |
51 | +{ | |
52 | + outb(reg, base); | |
53 | + return inb(base + 1); | |
54 | +} | |
55 | + | |
56 | +static inline int superio_enter(int base) | |
57 | +{ | |
58 | + /* Don't step on other drivers' I/O space by accident */ | |
59 | + if (!request_muxed_region(base, 2, "sch56xx")) { | |
60 | + pr_err("I/O address 0x%04x already in use\n", base); | |
61 | + return -EBUSY; | |
62 | + } | |
63 | + | |
64 | + outb(SIO_UNLOCK_KEY, base); | |
65 | + | |
66 | + return 0; | |
67 | +} | |
68 | + | |
69 | +static inline void superio_select(int base, int ld) | |
70 | +{ | |
71 | + outb(SIO_REG_LDSEL, base); | |
72 | + outb(ld, base + 1); | |
73 | +} | |
74 | + | |
75 | +static inline void superio_exit(int base) | |
76 | +{ | |
77 | + outb(SIO_LOCK_KEY, base); | |
78 | + release_region(base, 2); | |
79 | +} | |
80 | + | |
81 | +static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) | |
82 | +{ | |
83 | + u8 val; | |
84 | + int i; | |
85 | + /* | |
86 | + * According to SMSC for the commands we use the maximum time for | |
87 | + * the EM to respond is 15 ms, but testing shows in practice it | |
88 | + * responds within 15-32 reads, so we first busy poll, and if | |
89 | + * that fails sleep a bit and try again until we are way past | |
90 | + * the 15 ms maximum response time. | |
91 | + */ | |
92 | + const int max_busy_polls = 64; | |
93 | + const int max_lazy_polls = 32; | |
94 | + | |
95 | + /* (Optional) Write-Clear the EC to Host Mailbox Register */ | |
96 | + val = inb(addr + 1); | |
97 | + outb(val, addr + 1); | |
98 | + | |
99 | + /* Set Mailbox Address Pointer to first location in Region 1 */ | |
100 | + outb(0x00, addr + 2); | |
101 | + outb(0x80, addr + 3); | |
102 | + | |
103 | + /* Write Request Packet Header */ | |
104 | + outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */ | |
105 | + outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */ | |
106 | + outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */ | |
107 | + | |
108 | + /* Write Value field */ | |
109 | + if (cmd == SCH56XX_CMD_WRITE) | |
110 | + outb(v, addr + 4); | |
111 | + | |
112 | + /* Write Address field */ | |
113 | + outb(reg & 0xff, addr + 6); | |
114 | + outb(reg >> 8, addr + 7); | |
115 | + | |
116 | + /* Execute the Random Access Command */ | |
117 | + outb(0x01, addr); /* Write 01h to the Host-to-EC register */ | |
118 | + | |
119 | + /* EM Interface Polling "Algorithm" */ | |
120 | + for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { | |
121 | + if (i >= max_busy_polls) | |
122 | + msleep(1); | |
123 | + /* Read Interrupt source Register */ | |
124 | + val = inb(addr + 8); | |
125 | + /* Write Clear the interrupt source bits */ | |
126 | + if (val) | |
127 | + outb(val, addr + 8); | |
128 | + /* Command Completed ? */ | |
129 | + if (val & 0x01) | |
130 | + break; | |
131 | + } | |
132 | + if (i == max_busy_polls + max_lazy_polls) { | |
133 | + pr_err("Max retries exceeded reading virtual " | |
134 | + "register 0x%04hx (%d)\n", reg, 1); | |
135 | + return -EIO; | |
136 | + } | |
137 | + | |
138 | + /* | |
139 | + * According to SMSC we may need to retry this, but sofar I've always | |
140 | + * seen this succeed in 1 try. | |
141 | + */ | |
142 | + for (i = 0; i < max_busy_polls; i++) { | |
143 | + /* Read EC-to-Host Register */ | |
144 | + val = inb(addr + 1); | |
145 | + /* Command Completed ? */ | |
146 | + if (val == 0x01) | |
147 | + break; | |
148 | + | |
149 | + if (i == 0) | |
150 | + pr_warn("EC reports: 0x%02x reading virtual register " | |
151 | + "0x%04hx\n", (unsigned int)val, reg); | |
152 | + } | |
153 | + if (i == max_busy_polls) { | |
154 | + pr_err("Max retries exceeded reading virtual " | |
155 | + "register 0x%04hx (%d)\n", reg, 2); | |
156 | + return -EIO; | |
157 | + } | |
158 | + | |
159 | + /* | |
160 | + * According to the SMSC app note we should now do: | |
161 | + * | |
162 | + * Set Mailbox Address Pointer to first location in Region 1 * | |
163 | + * outb(0x00, addr + 2); | |
164 | + * outb(0x80, addr + 3); | |
165 | + * | |
166 | + * But if we do that things don't work, so let's not. | |
167 | + */ | |
168 | + | |
169 | + /* Read Value field */ | |
170 | + if (cmd == SCH56XX_CMD_READ) | |
171 | + return inb(addr + 4); | |
172 | + | |
173 | + return 0; | |
174 | +} | |
175 | + | |
176 | +int sch56xx_read_virtual_reg(u16 addr, u16 reg) | |
177 | +{ | |
178 | + return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0); | |
179 | +} | |
180 | +EXPORT_SYMBOL(sch56xx_read_virtual_reg); | |
181 | + | |
182 | +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val) | |
183 | +{ | |
184 | + return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val); | |
185 | +} | |
186 | +EXPORT_SYMBOL(sch56xx_write_virtual_reg); | |
187 | + | |
188 | +int sch56xx_read_virtual_reg16(u16 addr, u16 reg) | |
189 | +{ | |
190 | + int lsb, msb; | |
191 | + | |
192 | + /* Read LSB first, this will cause the matching MSB to be latched */ | |
193 | + lsb = sch56xx_read_virtual_reg(addr, reg); | |
194 | + if (lsb < 0) | |
195 | + return lsb; | |
196 | + | |
197 | + msb = sch56xx_read_virtual_reg(addr, reg + 1); | |
198 | + if (msb < 0) | |
199 | + return msb; | |
200 | + | |
201 | + return lsb | (msb << 8); | |
202 | +} | |
203 | +EXPORT_SYMBOL(sch56xx_read_virtual_reg16); | |
204 | + | |
205 | +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, | |
206 | + int high_nibble) | |
207 | +{ | |
208 | + int msb, lsn; | |
209 | + | |
210 | + /* Read MSB first, this will cause the matching LSN to be latched */ | |
211 | + msb = sch56xx_read_virtual_reg(addr, msb_reg); | |
212 | + if (msb < 0) | |
213 | + return msb; | |
214 | + | |
215 | + lsn = sch56xx_read_virtual_reg(addr, lsn_reg); | |
216 | + if (lsn < 0) | |
217 | + return lsn; | |
218 | + | |
219 | + if (high_nibble) | |
220 | + return (msb << 4) | (lsn >> 4); | |
221 | + else | |
222 | + return (msb << 4) | (lsn & 0x0f); | |
223 | +} | |
224 | +EXPORT_SYMBOL(sch56xx_read_virtual_reg12); | |
225 | + | |
226 | +static int __init sch56xx_find(int sioaddr, unsigned short *address, | |
227 | + const char **name) | |
228 | +{ | |
229 | + u8 devid; | |
230 | + int err; | |
231 | + | |
232 | + err = superio_enter(sioaddr); | |
233 | + if (err) | |
234 | + return err; | |
235 | + | |
236 | + devid = superio_inb(sioaddr, SIO_REG_DEVID); | |
237 | + switch (devid) { | |
238 | + case SIO_SCH5627_ID: | |
239 | + *name = "sch5627"; | |
240 | + break; | |
241 | + default: | |
242 | + pr_debug("Unsupported device id: 0x%02x\n", | |
243 | + (unsigned int)devid); | |
244 | + err = -ENODEV; | |
245 | + goto exit; | |
246 | + } | |
247 | + | |
248 | + superio_select(sioaddr, SIO_SCH56XX_LD_EM); | |
249 | + | |
250 | + if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { | |
251 | + pr_warn("Device not activated\n"); | |
252 | + err = -ENODEV; | |
253 | + goto exit; | |
254 | + } | |
255 | + | |
256 | + /* | |
257 | + * Warning the order of the low / high byte is the other way around | |
258 | + * as on most other superio devices!! | |
259 | + */ | |
260 | + *address = superio_inb(sioaddr, SIO_REG_ADDR) | | |
261 | + superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; | |
262 | + if (*address == 0) { | |
263 | + pr_warn("Base address not set\n"); | |
264 | + err = -ENODEV; | |
265 | + goto exit; | |
266 | + } | |
267 | + | |
268 | +exit: | |
269 | + superio_exit(sioaddr); | |
270 | + return err; | |
271 | +} | |
272 | + | |
273 | +static int __init sch56xx_device_add(unsigned short address, const char *name) | |
274 | +{ | |
275 | + struct resource res = { | |
276 | + .start = address, | |
277 | + .end = address + REGION_LENGTH - 1, | |
278 | + .flags = IORESOURCE_IO, | |
279 | + }; | |
280 | + int err; | |
281 | + | |
282 | + sch56xx_pdev = platform_device_alloc(name, address); | |
283 | + if (!sch56xx_pdev) | |
284 | + return -ENOMEM; | |
285 | + | |
286 | + res.name = sch56xx_pdev->name; | |
287 | + err = acpi_check_resource_conflict(&res); | |
288 | + if (err) | |
289 | + goto exit_device_put; | |
290 | + | |
291 | + err = platform_device_add_resources(sch56xx_pdev, &res, 1); | |
292 | + if (err) { | |
293 | + pr_err("Device resource addition failed\n"); | |
294 | + goto exit_device_put; | |
295 | + } | |
296 | + | |
297 | + err = platform_device_add(sch56xx_pdev); | |
298 | + if (err) { | |
299 | + pr_err("Device addition failed\n"); | |
300 | + goto exit_device_put; | |
301 | + } | |
302 | + | |
303 | + return 0; | |
304 | + | |
305 | +exit_device_put: | |
306 | + platform_device_put(sch56xx_pdev); | |
307 | + | |
308 | + return err; | |
309 | +} | |
310 | + | |
311 | +static int __init sch56xx_init(void) | |
312 | +{ | |
313 | + int err; | |
314 | + unsigned short address; | |
315 | + const char *name; | |
316 | + | |
317 | + err = sch56xx_find(0x4e, &address, &name); | |
318 | + if (err) | |
319 | + err = sch56xx_find(0x2e, &address, &name); | |
320 | + if (err) | |
321 | + return err; | |
322 | + | |
323 | + return sch56xx_device_add(address, name); | |
324 | +} | |
325 | + | |
326 | +static void __exit sch56xx_exit(void) | |
327 | +{ | |
328 | + platform_device_unregister(sch56xx_pdev); | |
329 | +} | |
330 | + | |
331 | +MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code"); | |
332 | +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | |
333 | +MODULE_LICENSE("GPL"); | |
334 | + | |
335 | +module_init(sch56xx_init); | |
336 | +module_exit(sch56xx_exit); |
drivers/hwmon/sch56xx-common.h
1 | +/*************************************************************************** | |
2 | + * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> * | |
3 | + * * | |
4 | + * This program is free software; you can redistribute it and/or modify * | |
5 | + * it under the terms of the GNU General Public License as published by * | |
6 | + * the Free Software Foundation; either version 2 of the License, or * | |
7 | + * (at your option) any later version. * | |
8 | + * * | |
9 | + * This program is distributed in the hope that it will be useful, * | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
12 | + * GNU General Public License for more details. * | |
13 | + * * | |
14 | + * You should have received a copy of the GNU General Public License * | |
15 | + * along with this program; if not, write to the * | |
16 | + * Free Software Foundation, Inc., * | |
17 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
18 | + ***************************************************************************/ | |
19 | + | |
20 | +int sch56xx_read_virtual_reg(u16 addr, u16 reg); | |
21 | +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val); | |
22 | +int sch56xx_read_virtual_reg16(u16 addr, u16 reg); | |
23 | +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, | |
24 | + int high_nibble); |