diff options
author | Christian Fetzer <fetzer.ch@gmail.com> | 2015-11-19 14:13:48 -0500 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2015-11-30 08:36:25 -0500 |
commit | 2fee61d22e606fc99ade9079fda15fdee83ec33e (patch) | |
tree | d4cc17f9b1f10caf71a2297b4a143f91b5015145 | |
parent | ca2061e1283b787b49e3f6817645b9f0a2151671 (diff) |
i2c: piix4: Add support for multiplexed main adapter in SB800
The SB800 chipset supports a multiplexed main SMBus controller with
four ports. The multiplexed ports share the same SMBus address and
register set. The port is selected by bits 2:1 of the smb_en register
(0x2C).
Only one port can be active at any point in time therefore a mutex is
needed in order to synchronize access.
Additionally, the commit avoids requesting and releasing the SMBus base
address index region on every multiplexed transfer by moving the
request_region call into piix4_probe.
Tested on HP ProLiant MicroServer G7 N54L (where this patch adds
support to access sensor data from the w83795adg).
Cc: Thomas Brandon <tbrandonau@gmail.com>
Cc: Eddi De Pieri <eddi@depieri.net>
Signed-off-by: Christian Fetzer <fetzer.ch@gmail.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r-- | drivers/i2c/busses/i2c-piix4.c | 169 |
1 files changed, 147 insertions, 22 deletions
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 9c32eb124b72..6b4231d406d5 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c | |||
@@ -23,6 +23,9 @@ | |||
23 | 23 | ||
24 | Note: we assume there can only be one device, with one or more | 24 | Note: we assume there can only be one device, with one or more |
25 | SMBus interfaces. | 25 | SMBus interfaces. |
26 | The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS). | ||
27 | For devices supporting multiple ports the i2c_adapter should provide | ||
28 | an i2c_algorithm to access them. | ||
26 | */ | 29 | */ |
27 | 30 | ||
28 | #include <linux/module.h> | 31 | #include <linux/module.h> |
@@ -37,6 +40,7 @@ | |||
37 | #include <linux/dmi.h> | 40 | #include <linux/dmi.h> |
38 | #include <linux/acpi.h> | 41 | #include <linux/acpi.h> |
39 | #include <linux/io.h> | 42 | #include <linux/io.h> |
43 | #include <linux/mutex.h> | ||
40 | 44 | ||
41 | 45 | ||
42 | /* PIIX4 SMBus address offsets */ | 46 | /* PIIX4 SMBus address offsets */ |
@@ -78,6 +82,13 @@ | |||
78 | /* Multi-port constants */ | 82 | /* Multi-port constants */ |
79 | #define PIIX4_MAX_ADAPTERS 4 | 83 | #define PIIX4_MAX_ADAPTERS 4 |
80 | 84 | ||
85 | /* SB800 constants */ | ||
86 | #define SB800_PIIX4_SMB_IDX 0xcd6 | ||
87 | |||
88 | /* SB800 port is selected by bits 2:1 of the smb_en register (0x2c) */ | ||
89 | #define SB800_PIIX4_PORT_IDX 0x2c | ||
90 | #define SB800_PIIX4_PORT_IDX_MASK 0x06 | ||
91 | |||
81 | /* insmod parameters */ | 92 | /* insmod parameters */ |
82 | 93 | ||
83 | /* If force is set to anything different from 0, we forcibly enable the | 94 | /* If force is set to anything different from 0, we forcibly enable the |
@@ -127,6 +138,11 @@ static const struct dmi_system_id piix4_dmi_ibm[] = { | |||
127 | 138 | ||
128 | struct i2c_piix4_adapdata { | 139 | struct i2c_piix4_adapdata { |
129 | unsigned short smba; | 140 | unsigned short smba; |
141 | |||
142 | /* SB800 */ | ||
143 | bool sb800_main; | ||
144 | unsigned short port; | ||
145 | struct mutex *mutex; | ||
130 | }; | 146 | }; |
131 | 147 | ||
132 | static int piix4_setup(struct pci_dev *PIIX4_dev, | 148 | static int piix4_setup(struct pci_dev *PIIX4_dev, |
@@ -232,7 +248,6 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, | |||
232 | const struct pci_device_id *id, u8 aux) | 248 | const struct pci_device_id *id, u8 aux) |
233 | { | 249 | { |
234 | unsigned short piix4_smba; | 250 | unsigned short piix4_smba; |
235 | unsigned short smba_idx = 0xcd6; | ||
236 | u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status; | 251 | u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status; |
237 | u8 i2ccfg, i2ccfg_offset = 0x10; | 252 | u8 i2ccfg, i2ccfg_offset = 0x10; |
238 | 253 | ||
@@ -254,16 +269,10 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, | |||
254 | else | 269 | else |
255 | smb_en = (aux) ? 0x28 : 0x2c; | 270 | smb_en = (aux) ? 0x28 : 0x2c; |
256 | 271 | ||
257 | if (!request_region(smba_idx, 2, "smba_idx")) { | 272 | outb_p(smb_en, SB800_PIIX4_SMB_IDX); |
258 | dev_err(&PIIX4_dev->dev, "SMBus base address index region " | 273 | smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); |
259 | "0x%x already in use!\n", smba_idx); | 274 | outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); |
260 | return -EBUSY; | 275 | smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); |
261 | } | ||
262 | outb_p(smb_en, smba_idx); | ||
263 | smba_en_lo = inb_p(smba_idx + 1); | ||
264 | outb_p(smb_en + 1, smba_idx); | ||
265 | smba_en_hi = inb_p(smba_idx + 1); | ||
266 | release_region(smba_idx, 2); | ||
267 | 276 | ||
268 | if (!smb_en) { | 277 | if (!smb_en) { |
269 | smb_en_status = smba_en_lo & 0x10; | 278 | smb_en_status = smba_en_lo & 0x10; |
@@ -527,6 +536,43 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr, | |||
527 | return 0; | 536 | return 0; |
528 | } | 537 | } |
529 | 538 | ||
539 | /* | ||
540 | * Handles access to multiple SMBus ports on the SB800. | ||
541 | * The port is selected by bits 2:1 of the smb_en register (0x2c). | ||
542 | * Returns negative errno on error. | ||
543 | * | ||
544 | * Note: The selected port must be returned to the initial selection to avoid | ||
545 | * problems on certain systems. | ||
546 | */ | ||
547 | static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, | ||
548 | unsigned short flags, char read_write, | ||
549 | u8 command, int size, union i2c_smbus_data *data) | ||
550 | { | ||
551 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); | ||
552 | u8 smba_en_lo; | ||
553 | u8 port; | ||
554 | int retval; | ||
555 | |||
556 | mutex_lock(adapdata->mutex); | ||
557 | |||
558 | outb_p(SB800_PIIX4_PORT_IDX, SB800_PIIX4_SMB_IDX); | ||
559 | smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); | ||
560 | |||
561 | port = adapdata->port; | ||
562 | if ((smba_en_lo & SB800_PIIX4_PORT_IDX_MASK) != (port << 1)) | ||
563 | outb_p((smba_en_lo & ~SB800_PIIX4_PORT_IDX_MASK) | (port << 1), | ||
564 | SB800_PIIX4_SMB_IDX + 1); | ||
565 | |||
566 | retval = piix4_access(adap, addr, flags, read_write, | ||
567 | command, size, data); | ||
568 | |||
569 | outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1); | ||
570 | |||
571 | mutex_unlock(adapdata->mutex); | ||
572 | |||
573 | return retval; | ||
574 | } | ||
575 | |||
530 | static u32 piix4_func(struct i2c_adapter *adapter) | 576 | static u32 piix4_func(struct i2c_adapter *adapter) |
531 | { | 577 | { |
532 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | | 578 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | |
@@ -539,6 +585,11 @@ static const struct i2c_algorithm smbus_algorithm = { | |||
539 | .functionality = piix4_func, | 585 | .functionality = piix4_func, |
540 | }; | 586 | }; |
541 | 587 | ||
588 | static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = { | ||
589 | .smbus_xfer = piix4_access_sb800, | ||
590 | .functionality = piix4_func, | ||
591 | }; | ||
592 | |||
542 | static const struct pci_device_id piix4_ids[] = { | 593 | static const struct pci_device_id piix4_ids[] = { |
543 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, | 594 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, |
544 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, | 595 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, |
@@ -614,6 +665,53 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, | |||
614 | return 0; | 665 | return 0; |
615 | } | 666 | } |
616 | 667 | ||
668 | static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba) | ||
669 | { | ||
670 | struct mutex *mutex; | ||
671 | struct i2c_piix4_adapdata *adapdata; | ||
672 | int port; | ||
673 | int retval; | ||
674 | |||
675 | mutex = kzalloc(sizeof(*mutex), GFP_KERNEL); | ||
676 | if (mutex == NULL) | ||
677 | return -ENOMEM; | ||
678 | |||
679 | mutex_init(mutex); | ||
680 | |||
681 | for (port = 0; port < PIIX4_MAX_ADAPTERS; port++) { | ||
682 | retval = piix4_add_adapter(dev, smba, | ||
683 | &piix4_main_adapters[port]); | ||
684 | if (retval < 0) | ||
685 | goto error; | ||
686 | |||
687 | piix4_main_adapters[port]->algo = &piix4_smbus_algorithm_sb800; | ||
688 | |||
689 | adapdata = i2c_get_adapdata(piix4_main_adapters[port]); | ||
690 | adapdata->sb800_main = true; | ||
691 | adapdata->port = port; | ||
692 | adapdata->mutex = mutex; | ||
693 | } | ||
694 | |||
695 | return retval; | ||
696 | |||
697 | error: | ||
698 | dev_err(&dev->dev, | ||
699 | "Error setting up SB800 adapters. Unregistering!\n"); | ||
700 | while (--port >= 0) { | ||
701 | adapdata = i2c_get_adapdata(piix4_main_adapters[port]); | ||
702 | if (adapdata->smba) { | ||
703 | i2c_del_adapter(piix4_main_adapters[port]); | ||
704 | kfree(adapdata); | ||
705 | kfree(piix4_main_adapters[port]); | ||
706 | piix4_main_adapters[port] = NULL; | ||
707 | } | ||
708 | } | ||
709 | |||
710 | kfree(mutex); | ||
711 | |||
712 | return retval; | ||
713 | } | ||
714 | |||
617 | static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) | 715 | static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) |
618 | { | 716 | { |
619 | int retval; | 717 | int retval; |
@@ -621,20 +719,41 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
621 | if ((dev->vendor == PCI_VENDOR_ID_ATI && | 719 | if ((dev->vendor == PCI_VENDOR_ID_ATI && |
622 | dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && | 720 | dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && |
623 | dev->revision >= 0x40) || | 721 | dev->revision >= 0x40) || |
624 | dev->vendor == PCI_VENDOR_ID_AMD) | 722 | dev->vendor == PCI_VENDOR_ID_AMD) { |
723 | if (!request_region(SB800_PIIX4_SMB_IDX, 2, "smba_idx")) { | ||
724 | dev_err(&dev->dev, | ||
725 | "SMBus base address index region 0x%x already in use!\n", | ||
726 | SB800_PIIX4_SMB_IDX); | ||
727 | return -EBUSY; | ||
728 | } | ||
729 | |||
625 | /* base address location etc changed in SB800 */ | 730 | /* base address location etc changed in SB800 */ |
626 | retval = piix4_setup_sb800(dev, id, 0); | 731 | retval = piix4_setup_sb800(dev, id, 0); |
627 | else | 732 | if (retval < 0) { |
628 | retval = piix4_setup(dev, id); | 733 | release_region(SB800_PIIX4_SMB_IDX, 2); |
734 | return retval; | ||
735 | } | ||
629 | 736 | ||
630 | /* If no main SMBus found, give up */ | 737 | /* |
631 | if (retval < 0) | 738 | * Try to register multiplexed main SMBus adapter, |
632 | return retval; | 739 | * give up if we can't |
740 | */ | ||
741 | retval = piix4_add_adapters_sb800(dev, retval); | ||
742 | if (retval < 0) { | ||
743 | release_region(SB800_PIIX4_SMB_IDX, 2); | ||
744 | return retval; | ||
745 | } | ||
746 | } else { | ||
747 | retval = piix4_setup(dev, id); | ||
748 | if (retval < 0) | ||
749 | return retval; | ||
633 | 750 | ||
634 | /* Try to register main SMBus adapter, give up if we can't */ | 751 | /* Try to register main SMBus adapter, give up if we can't */ |
635 | retval = piix4_add_adapter(dev, retval, &piix4_main_adapters[0]); | 752 | retval = piix4_add_adapter(dev, retval, |
636 | if (retval < 0) | 753 | &piix4_main_adapters[0]); |
637 | return retval; | 754 | if (retval < 0) |
755 | return retval; | ||
756 | } | ||
638 | 757 | ||
639 | /* Check for auxiliary SMBus on some AMD chipsets */ | 758 | /* Check for auxiliary SMBus on some AMD chipsets */ |
640 | retval = -ENODEV; | 759 | retval = -ENODEV; |
@@ -669,7 +788,13 @@ static void piix4_adap_remove(struct i2c_adapter *adap) | |||
669 | 788 | ||
670 | if (adapdata->smba) { | 789 | if (adapdata->smba) { |
671 | i2c_del_adapter(adap); | 790 | i2c_del_adapter(adap); |
672 | release_region(adapdata->smba, SMBIOSIZE); | 791 | if (adapdata->port == 0) { |
792 | release_region(adapdata->smba, SMBIOSIZE); | ||
793 | if (adapdata->sb800_main) { | ||
794 | kfree(adapdata->mutex); | ||
795 | release_region(SB800_PIIX4_SMB_IDX, 2); | ||
796 | } | ||
797 | } | ||
673 | kfree(adapdata); | 798 | kfree(adapdata); |
674 | kfree(adap); | 799 | kfree(adap); |
675 | } | 800 | } |