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 /drivers/i2c | |
| 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>
Diffstat (limited to 'drivers/i2c')
| -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 | } |
