cregit-Linux how code gets into the kernel

Release 4.11 drivers/i2c/busses/i2c-nforce2.c

/*
    SMBus driver for nVidia nForce2 MCP

    Added nForce3 Pro 150  Thomas Leibold <thomas@plx.com>,
        Ported to 2.5 Patrick Dreker <patrick@dreker.de>,
    Copyright (c) 2003  Hans-Frieder Vogt <hfvogt@arcor.de>,
    Based on
    SMBus 2.0 driver for AMD-8111 IO-Hub
    Copyright (c) 2002 Vojtech Pavlik

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
*/

/*
    SUPPORTED DEVICES           PCI ID
    nForce2 MCP                 0064
    nForce2 Ultra 400 MCP       0084
    nForce3 Pro150 MCP          00D4
    nForce3 250Gb MCP           00E4
    nForce4 MCP                 0052
    nForce4 MCP-04              0034
    nForce MCP51                0264
    nForce MCP55                0368
    nForce MCP61                03EB
    nForce MCP65                0446
    nForce MCP67                0542
    nForce MCP73                07D8
    nForce MCP78S               0752
    nForce MCP79                0AA2

    This driver supports the 2 SMBuses that are included in the MCP of the
    nForce2/3/4/5xx chipsets.
*/

/* Note: we assume there can only be one nForce2, with two SMBus interfaces */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/io.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>");
MODULE_DESCRIPTION("nForce2/3/4/5xx SMBus driver");



struct nforce2_smbus {
	
struct i2c_adapter adapter;
	
int base;
	
int size;
	
int blockops;
	
int can_abort;
};


/*
 * nVidia nForce2 SMBus control register definitions
 * (Newer incarnations use standard BARs 4 and 5 instead)
 */

#define NFORCE_PCI_SMB1	0x50

#define NFORCE_PCI_SMB2	0x54


/*
 * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
 */

#define NVIDIA_SMB_PRTCL	(smbus->base + 0x00)	
/* protocol, PEC */

#define NVIDIA_SMB_STS		(smbus->base + 0x01)	
/* status */

#define NVIDIA_SMB_ADDR		(smbus->base + 0x02)	
/* address */

#define NVIDIA_SMB_CMD		(smbus->base + 0x03)	
/* command */

#define NVIDIA_SMB_DATA		(smbus->base + 0x04)	
/* 32 data registers */

#define NVIDIA_SMB_BCNT		(smbus->base + 0x24)	
/* number of data
                                                           bytes */

#define NVIDIA_SMB_STATUS_ABRT	(smbus->base + 0x3c)	
/* register used to
                                                           check the status of
                                                           the abort command */

#define NVIDIA_SMB_CTRL		(smbus->base + 0x3e)	
/* control register */


#define NVIDIA_SMB_STATUS_ABRT_STS	0x01		
/* Bit to notify that
                                                           abort succeeded */

#define NVIDIA_SMB_CTRL_ABORT	0x20

#define NVIDIA_SMB_STS_DONE	0x80

#define NVIDIA_SMB_STS_ALRM	0x40

#define NVIDIA_SMB_STS_RES	0x20

#define NVIDIA_SMB_STS_STATUS	0x1f


#define NVIDIA_SMB_PRTCL_WRITE			0x00

#define NVIDIA_SMB_PRTCL_READ			0x01

#define NVIDIA_SMB_PRTCL_QUICK			0x02

#define NVIDIA_SMB_PRTCL_BYTE			0x04

#define NVIDIA_SMB_PRTCL_BYTE_DATA		0x06

#define NVIDIA_SMB_PRTCL_WORD_DATA		0x08

#define NVIDIA_SMB_PRTCL_BLOCK_DATA		0x0a

#define NVIDIA_SMB_PRTCL_PEC			0x80

/* Misc definitions */

#define MAX_TIMEOUT	100

/* We disable the second SMBus channel on these boards */

static const struct dmi_system_id nforce2_dmi_blacklist2[] = {
	{
		.ident = "DFI Lanparty NF4 Expert",
		.matches = {
			DMI_MATCH(DMI_BOARD_VENDOR, "DFI Corp,LTD"),
			DMI_MATCH(DMI_BOARD_NAME, "LP UT NF4 Expert"),
                },
        },
	{ }
};


static struct pci_driver nforce2_driver;

/* For multiplexing support, we need a global reference to the 1st
   SMBus channel */
#if IS_ENABLED(CONFIG_I2C_NFORCE2_S4985)

struct i2c_adapter *nforce2_smbus;

EXPORT_SYMBOL_GPL(nforce2_smbus);


static void nforce2_set_reference(struct i2c_adapter *adap) { nforce2_smbus = adap; }

Contributors

PersonTokensPropCommitsCommitProp
Jean Delvare15100.00%1100.00%
Total15100.00%1100.00%

#else
static inline void nforce2_set_reference(struct i2c_adapter *adap) { }

Contributors

PersonTokensPropCommitsCommitProp
Jean Delvare11100.00%1100.00%
Total11100.00%1100.00%

#endif
static void nforce2_abort(struct i2c_adapter *adap) { struct nforce2_smbus *smbus = adap->algo_data; int timeout = 0; unsigned char temp; dev_dbg(&adap->dev, "Aborting current transaction\n"); outb_p(NVIDIA_SMB_CTRL_ABORT, NVIDIA_SMB_CTRL); do { msleep(1); temp = inb_p(NVIDIA_SMB_STATUS_ABRT); } while (!(temp & NVIDIA_SMB_STATUS_ABRT_STS) && (timeout++ < MAX_TIMEOUT)); if (!(temp & NVIDIA_SMB_STATUS_ABRT_STS)) dev_err(&adap->dev, "Can't reset the smbus\n"); outb_p(NVIDIA_SMB_STATUS_ABRT_STS, NVIDIA_SMB_STATUS_ABRT); }

Contributors

PersonTokensPropCommitsCommitProp
Oleg Ryjkov104100.00%1100.00%
Total104100.00%1100.00%


static int nforce2_check_status(struct i2c_adapter *adap) { struct nforce2_smbus *smbus = adap->algo_data; int timeout = 0; unsigned char temp; do { msleep(1); temp = inb_p(NVIDIA_SMB_STS); } while ((!temp) && (timeout++ < MAX_TIMEOUT)); if (timeout > MAX_TIMEOUT) { dev_dbg(&adap->dev, "SMBus Timeout!\n"); if (smbus->can_abort) nforce2_abort(adap); return -ETIMEDOUT; } if (!(temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS)) { dev_dbg(&adap->dev, "Transaction failed (0x%02x)!\n", temp); return -EIO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Oleg Ryjkov12597.66%250.00%
David Brownell21.56%125.00%
Roel Kluin10.78%125.00%
Total128100.00%4100.00%

/* Return negative errno on error */
static s32 nforce2_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { struct nforce2_smbus *smbus = adap->algo_data; unsigned char protocol, pec; u8 len; int i, status; protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : NVIDIA_SMB_PRTCL_WRITE; pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0; switch (size) { case I2C_SMBUS_QUICK: protocol |= NVIDIA_SMB_PRTCL_QUICK; read_write = I2C_SMBUS_WRITE; break; case I2C_SMBUS_BYTE: if (read_write == I2C_SMBUS_WRITE) outb_p(command, NVIDIA_SMB_CMD); protocol |= NVIDIA_SMB_PRTCL_BYTE; break; case I2C_SMBUS_BYTE_DATA: outb_p(command, NVIDIA_SMB_CMD); if (read_write == I2C_SMBUS_WRITE) outb_p(data->byte, NVIDIA_SMB_DATA); protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA; break; case I2C_SMBUS_WORD_DATA: outb_p(command, NVIDIA_SMB_CMD); if (read_write == I2C_SMBUS_WRITE) { outb_p(data->word, NVIDIA_SMB_DATA); outb_p(data->word >> 8, NVIDIA_SMB_DATA + 1); } protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec; break; case I2C_SMBUS_BLOCK_DATA: outb_p(command, NVIDIA_SMB_CMD); if (read_write == I2C_SMBUS_WRITE) { len = data->block[0]; if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) { dev_err(&adap->dev, "Transaction failed (requested block size: %d)\n", len); return -EINVAL; } outb_p(len, NVIDIA_SMB_BCNT); for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++) outb_p(data->block[i + 1], NVIDIA_SMB_DATA + i); } protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec; break; default: dev_err(&adap->dev, "Unsupported transaction %d\n", size); return -EOPNOTSUPP; } outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR); outb_p(protocol, NVIDIA_SMB_PRTCL); status = nforce2_check_status(adap); if (status) return status; if (read_write == I2C_SMBUS_WRITE) return 0; switch (size) { case I2C_SMBUS_BYTE: case I2C_SMBUS_BYTE_DATA: data->byte = inb_p(NVIDIA_SMB_DATA); break; case I2C_SMBUS_WORD_DATA: data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA + 1) << 8); break; case I2C_SMBUS_BLOCK_DATA: len = inb_p(NVIDIA_SMB_BCNT); if ((len <= 0) || (len > I2C_SMBUS_BLOCK_MAX)) { dev_err(&adap->dev, "Transaction failed (received block size: 0x%02x)\n", len); return -EPROTO; } for (i = 0; i < len; i++) data->block[i + 1] = inb_p(NVIDIA_SMB_DATA + i); data->block[0] = len; break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Dreker28157.70%114.29%
Oleg Ryjkov18738.40%228.57%
David Brownell122.46%114.29%
Hans-Frieder Vogt30.62%114.29%
Jean Delvare20.41%114.29%
Laurent Navet20.41%114.29%
Total487100.00%7100.00%


static u32 nforce2_func(struct i2c_adapter *adapter) { /* other functionality might be possible, but is not tested */ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PEC | (((struct nforce2_smbus *)adapter->algo_data)->blockops ? I2C_FUNC_SMBUS_BLOCK_DATA : 0); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Dreker2150.00%133.33%
Oleg Ryjkov1945.24%133.33%
Jean Delvare24.76%133.33%
Total42100.00%3100.00%

static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = nforce2_access, .functionality = nforce2_func, }; static const struct pci_device_id nforce2_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS) }, { 0 } }; MODULE_DEVICE_TABLE(pci, nforce2_ids);
static int nforce2_probe_smb(struct pci_dev *dev, int bar, int alt_reg, struct nforce2_smbus *smbus, const char *name) { int error; smbus->base = pci_resource_start(dev, bar); if (smbus->base) { smbus->size = pci_resource_len(dev, bar); } else { /* Older incarnations of the device used non-standard BARs */ u16 iobase; if (pci_read_config_word(dev, alt_reg, &iobase) != PCIBIOS_SUCCESSFUL) { dev_err(&dev->dev, "Error reading PCI config for %s\n", name); return -EIO; } smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK; smbus->size = 64; } error = acpi_check_region(smbus->base, smbus->size, nforce2_driver.name); if (error) return error; if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) { dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n", smbus->base, smbus->base+smbus->size-1, name); return -EBUSY; } smbus->adapter.owner = THIS_MODULE; smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; smbus->adapter.dev.parent = &dev->dev; snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), "SMBus nForce2 adapter at %04x", smbus->base); error = i2c_add_adapter(&smbus->adapter); if (error) { release_region(smbus->base, smbus->size); return error; } dev_info(&smbus->adapter.dev, "nForce2 SMBus adapter at %#x\n", smbus->base); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Dreker19663.23%110.00%
Jean Delvare7423.87%550.00%
Hans-Frieder Vogt216.77%110.00%
David Brownell113.55%220.00%
Mark M. Hoffman82.58%110.00%
Total310100.00%10100.00%


static int nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct nforce2_smbus *smbuses; int res1, res2; /* we support 2 SMBus adapters */ smbuses = kzalloc(2 * sizeof(struct nforce2_smbus), GFP_KERNEL); if (!smbuses) return -ENOMEM; pci_set_drvdata(dev, smbuses); switch (dev->device) { case PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS: case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS: case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS: smbuses[0].blockops = 1; smbuses[1].blockops = 1; smbuses[0].can_abort = 1; smbuses[1].can_abort = 1; } /* SMBus adapter 1 */ res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1"); if (res1 < 0) smbuses[0].base = 0; /* to have a check value */ /* SMBus adapter 2 */ if (dmi_check_system(nforce2_dmi_blacklist2)) { dev_err(&dev->dev, "Disabling SMB2 for safety reasons.\n"); res2 = -EPERM; smbuses[1].base = 0; } else { res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], "SMB2"); if (res2 < 0) smbuses[1].base = 0; /* to have a check value */ } if ((res1 < 0) && (res2 < 0)) { /* we did not find even one of the SMBuses, so we give up */ kfree(smbuses); return -ENODEV; } nforce2_set_reference(&smbuses[0].adapter); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Dreker15057.69%111.11%
Jean Delvare5420.77%444.44%
Oleg Ryjkov5019.23%222.22%
Laurent Navet51.92%111.11%
Deepak Saxena10.38%111.11%
Total260100.00%9100.00%


static void nforce2_remove(struct pci_dev *dev) { struct nforce2_smbus *smbuses = pci_get_drvdata(dev); nforce2_set_reference(NULL); if (smbuses[0].base) { i2c_del_adapter(&smbuses[0].adapter); release_region(smbuses[0].base, smbuses[0].size); } if (smbuses[1].base) { i2c_del_adapter(&smbuses[1].adapter); release_region(smbuses[1].base, smbuses[1].size); } kfree(smbuses); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Dreker10495.41%150.00%
Jean Delvare54.59%150.00%
Total109100.00%2100.00%

static struct pci_driver nforce2_driver = { .name = "nForce2_smbus", .id_table = nforce2_ids, .probe = nforce2_probe, .remove = nforce2_remove, }; module_pci_driver(nforce2_driver);

Overall Contributors

PersonTokensPropCommitsCommitProp
Patrick Dreker92148.19%12.50%
Oleg Ryjkov52427.42%37.50%
Jean Delvare34217.90%1640.00%
Hans-Frieder Vogt522.72%37.50%
David Brownell261.36%25.00%
Mark M. Hoffman80.42%12.50%
Matthieu Castet70.37%12.50%
Laurent Navet70.37%12.50%
Jingoo Han60.31%12.50%
Tejun Heo30.16%12.50%
Javier Martinez Canillas30.16%12.50%
Thomas Leibold30.16%12.50%
Deepak Saxena20.10%25.00%
Axel Lin20.10%12.50%
Wolfram Sang10.05%12.50%
H Hartley Sweeten10.05%12.50%
Roel Kluin10.05%12.50%
Bhumika Goyal10.05%12.50%
Bill Pemberton10.05%12.50%
Greg Kroah-Hartman0.00%00.00%
Total1911100.00%40100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.