X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fi2c%2Fbusses%2Fscx200_acb.c;h=49f8a50205e7402d4a5cb7d67777d8311cd49dcb;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=9b4f4ee4f6664ed0e4910cab70f15ccbe884ac0e;hpb=f7ed79d23a47594e7834d66a8f14449796d4f3e6;p=linux-2.6.git diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index 9b4f4ee4f..49f8a5020 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -1,26 +1,27 @@ -/* +/* linux/drivers/i2c/scx200_acb.c + Copyright (c) 2001,2002 Christer Weinigel National Semiconductor SCx200 ACCESS.bus support - Also supports the AMD CS5535 and AMD CS5536 - + Based on i2c-keywest.c which is: Copyright (c) 2001 Benjamin Herrenschmidt Copyright (c) 2000 Philip Edelbrock - + 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. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ #include @@ -31,9 +32,7 @@ #include #include #include -#include #include -#include #include @@ -48,7 +47,16 @@ static int base[MAX_DEVICES] = { 0x820, 0x840 }; module_param_array(base, int, NULL, 0); MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers"); -#define POLL_TIMEOUT (HZ/5) +#ifdef DEBUG +#define DBG(x...) printk(KERN_DEBUG NAME ": " x) +#else +#define DBG(x...) +#endif + +/* The hardware supports interrupt driven mode too, but I haven't + implemented that. */ +#define POLLED_MODE 1 +#define POLL_TIMEOUT (HZ) enum scx200_acb_state { state_idle, @@ -71,11 +79,12 @@ static const char *scx200_acb_state_name[] = { }; /* Physical interface */ -struct scx200_acb_iface { +struct scx200_acb_iface +{ struct scx200_acb_iface *next; struct i2c_adapter adapter; unsigned base; - struct mutex mutex; + struct semaphore sem; /* State machine data */ enum scx200_acb_state state; @@ -91,7 +100,7 @@ struct scx200_acb_iface { #define ACBSDA (iface->base + 0) #define ACBST (iface->base + 1) #define ACBST_SDAST 0x40 /* SDA Status */ -#define ACBST_BER 0x20 +#define ACBST_BER 0x20 #define ACBST_NEGACK 0x10 /* Negative Acknowledge */ #define ACBST_STASTR 0x08 /* Stall After Start */ #define ACBST_MASTER 0x02 @@ -100,9 +109,9 @@ struct scx200_acb_iface { #define ACBCTL1 (iface->base + 3) #define ACBCTL1_STASTRE 0x80 #define ACBCTL1_NMINTE 0x40 -#define ACBCTL1_ACK 0x10 -#define ACBCTL1_STOP 0x02 -#define ACBCTL1_START 0x01 +#define ACBCTL1_ACK 0x10 +#define ACBCTL1_STOP 0x02 +#define ACBCTL1_START 0x01 #define ACBADDR (iface->base + 4) #define ACBCTL2 (iface->base + 5) #define ACBCTL2_ENABLE 0x01 @@ -113,8 +122,8 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) { const char *errmsg; - dev_dbg(&iface->adapter.dev, "state %s, status = 0x%02x\n", - scx200_acb_state_name[iface->state], status); + DBG("state %s, status = 0x%02x\n", + scx200_acb_state_name[iface->state], status); if (status & ACBST_BER) { errmsg = "bus error"; @@ -124,20 +133,8 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) errmsg = "not master"; goto error; } - if (status & ACBST_NEGACK) { - dev_dbg(&iface->adapter.dev, "negative ack in state %s\n", - scx200_acb_state_name[iface->state]); - - iface->state = state_idle; - iface->result = -ENXIO; - - outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); - outb(ACBST_STASTR | ACBST_NEGACK, ACBST); - - /* Reset the status register */ - outb(0, ACBST); - return; - } + if (status & ACBST_NEGACK) + goto negack; switch (iface->state) { case state_idle: @@ -163,10 +160,10 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) case state_repeat_start: outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); /* fallthrough */ - + case state_quick: if (iface->address_byte & 1) { - if (iface->len == 1) + if (iface->len == 1) outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); else outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); @@ -205,15 +202,26 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); break; } - + outb(*iface->ptr++, ACBSDA); --iface->len; - + break; } return; + negack: + DBG("negative acknowledge in state %s\n", + scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -ENXIO; + + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + outb(ACBST_STASTR | ACBST_NEGACK, ACBST); + return; + error: dev_err(&iface->adapter.dev, "%s in state %s\n", errmsg, scx200_acb_state_name[iface->state]); @@ -223,37 +231,40 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) iface->needs_reset = 1; } +static void scx200_acb_timeout(struct scx200_acb_iface *iface) +{ + dev_err(&iface->adapter.dev, "timeout in state %s\n", + scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +#ifdef POLLED_MODE static void scx200_acb_poll(struct scx200_acb_iface *iface) { - u8 status; + u8 status = 0; unsigned long timeout; timeout = jiffies + POLL_TIMEOUT; while (time_before(jiffies, timeout)) { status = inb(ACBST); - - /* Reset the status register to avoid the hang */ - outb(0, ACBST); - if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) { scx200_acb_machine(iface, status); return; } - yield(); + msleep(10); } - dev_err(&iface->adapter.dev, "timeout in state %s\n", - scx200_acb_state_name[iface->state]); - - iface->state = state_idle; - iface->result = -EIO; - iface->needs_reset = 1; + scx200_acb_timeout(iface); } +#endif /* POLLED_MODE */ static void scx200_acb_reset(struct scx200_acb_iface *iface) { /* Disable the ACCESS.bus device and Configure the SCL - frequency: 16 clock cycles */ + frequency: 16 clock cycles */ outb(0x70, ACBCTL2); /* Polling mode */ outb(0, ACBCTL1); @@ -272,9 +283,9 @@ static void scx200_acb_reset(struct scx200_acb_iface *iface) } static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter, - u16 address, unsigned short flags, - char rw, u8 command, int size, - union i2c_smbus_data *data) + u16 address, unsigned short flags, + char rw, u8 command, int size, + union i2c_smbus_data *data) { struct scx200_acb_iface *iface = i2c_get_adapdata(adapter); int len; @@ -284,51 +295,57 @@ static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter, switch (size) { case I2C_SMBUS_QUICK: - len = 0; - buffer = NULL; - break; - + len = 0; + buffer = NULL; + break; case I2C_SMBUS_BYTE: - len = 1; - buffer = rw ? &data->byte : &command; - break; - + if (rw == I2C_SMBUS_READ) { + len = 1; + buffer = &data->byte; + } else { + len = 1; + buffer = &command; + } + break; case I2C_SMBUS_BYTE_DATA: - len = 1; - buffer = &data->byte; - break; - + len = 1; + buffer = &data->byte; + break; case I2C_SMBUS_WORD_DATA: len = 2; - cur_word = cpu_to_le16(data->word); - buffer = (u8 *)&cur_word; + cur_word = cpu_to_le16(data->word); + buffer = (u8 *)&cur_word; break; - case I2C_SMBUS_I2C_BLOCK_DATA: if (rw == I2C_SMBUS_READ) data->block[0] = I2C_SMBUS_BLOCK_MAX; /* For now */ - len = data->block[0]; + len = data->block[0]; if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) return -EINVAL; - buffer = &data->block[1]; + buffer = &data->block[1]; break; - default: - return -EINVAL; + return -EINVAL; } - dev_dbg(&adapter->dev, - "size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n", - size, address, command, len, rw); + DBG("size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n", + size, address, command, len, rw == I2C_SMBUS_READ); if (!len && rw == I2C_SMBUS_READ) { - dev_dbg(&adapter->dev, "zero length read\n"); + dev_warn(&adapter->dev, "zero length read\n"); return -EINVAL; } - mutex_lock(&iface->mutex); + if (len && !buffer) { + dev_warn(&adapter->dev, "nonzero length but no buffer\n"); + return -EFAULT; + } + + down(&iface->sem); - iface->address_byte = (address << 1) | rw; + iface->address_byte = address<<1; + if (rw == I2C_SMBUS_READ) + iface->address_byte |= 1; iface->command = command; iface->ptr = buffer; iface->len = len; @@ -342,21 +359,25 @@ static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter, else iface->state = state_address; +#ifdef POLLED_MODE while (iface->state != state_idle) scx200_acb_poll(iface); +#else /* POLLED_MODE */ +#error Interrupt driven mode not implemented +#endif /* POLLED_MODE */ if (iface->needs_reset) scx200_acb_reset(iface); rc = iface->result; - mutex_unlock(&iface->mutex); + up(&iface->sem); if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ) - data->word = le16_to_cpu(cur_word); + data->word = le16_to_cpu(cur_word); #ifdef DEBUG - dev_dbg(&adapter->dev, "transfer done, result: %d", rc); + DBG(": transfer done, result: %d", rc); if (buffer) { int i; printk(" data:"); @@ -383,18 +404,17 @@ static struct i2c_algorithm scx200_acb_algorithm = { }; static struct scx200_acb_iface *scx200_acb_list; -static DECLARE_MUTEX(scx200_acb_list_mutex); static int scx200_acb_probe(struct scx200_acb_iface *iface) { u8 val; /* Disable the ACCESS.bus device and Configure the SCL - frequency: 16 clock cycles */ + frequency: 16 clock cycles */ outb(0x70, ACBCTL2); if (inb(ACBCTL2) != 0x70) { - pr_debug(NAME ": ACBCTL2 readback failed\n"); + DBG("ACBCTL2 readback failed\n"); return -ENXIO; } @@ -402,8 +422,7 @@ static int scx200_acb_probe(struct scx200_acb_iface *iface) val = inb(ACBCTL1); if (val) { - pr_debug(NAME ": disabled, but ACBCTL1=0x%02x\n", - val); + DBG("disabled, but ACBCTL1=0x%02x\n", val); return -ENXIO; } @@ -413,19 +432,18 @@ static int scx200_acb_probe(struct scx200_acb_iface *iface) val = inb(ACBCTL1); if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) { - pr_debug(NAME ": enabled, but NMINTE won't be set, " - "ACBCTL1=0x%02x\n", val); + DBG("enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", val); return -ENXIO; } return 0; } -static int __init scx200_acb_create(const char *text, int base, int index) +static int __init scx200_acb_create(int base, int index) { struct scx200_acb_iface *iface; struct i2c_adapter *adapter; - int rc; + int rc = 0; iface = kzalloc(sizeof(*iface), GFP_KERNEL); if (!iface) { @@ -436,48 +454,49 @@ static int __init scx200_acb_create(const char *text, int base, int index) adapter = &iface->adapter; i2c_set_adapdata(adapter, iface); - snprintf(adapter->name, I2C_NAME_SIZE, "%s ACB%d", text, index); + snprintf(adapter->name, I2C_NAME_SIZE, "SCx200 ACB%d", index); adapter->owner = THIS_MODULE; adapter->id = I2C_HW_SMBUS_SCX200; adapter->algo = &scx200_acb_algorithm; adapter->class = I2C_CLASS_HWMON; - mutex_init(&iface->mutex); + init_MUTEX(&iface->sem); if (!request_region(base, 8, adapter->name)) { - printk(KERN_ERR NAME ": can't allocate io 0x%x-0x%x\n", + dev_err(&adapter->dev, "can't allocate io 0x%x-0x%x\n", base, base + 8-1); rc = -EBUSY; - goto errout_free; + goto errout; } iface->base = base; rc = scx200_acb_probe(iface); if (rc) { - printk(KERN_WARNING NAME ": probe failed\n"); - goto errout_release; + dev_warn(&adapter->dev, "probe failed\n"); + goto errout; } scx200_acb_reset(iface); if (i2c_add_adapter(adapter) < 0) { - printk(KERN_ERR NAME ": failed to register\n"); + dev_err(&adapter->dev, "failed to register\n"); rc = -ENODEV; - goto errout_release; + goto errout; } - down(&scx200_acb_list_mutex); + lock_kernel(); iface->next = scx200_acb_list; scx200_acb_list = iface; - up(&scx200_acb_list_mutex); + unlock_kernel(); return 0; - errout_release: - release_region(iface->base, 8); - errout_free: - kfree(iface); errout: + if (iface) { + if (iface->base) + release_region(iface->base, 8); + kfree(iface); + } return rc; } @@ -487,51 +506,22 @@ static struct pci_device_id scx200[] = { { }, }; -static struct pci_device_id divil_pci[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, - { } /* NULL entry */ -}; - -#define MSR_LBAR_SMB 0x5140000B - -static __init int scx200_add_cs553x(void) -{ - u32 low, hi; - u32 smb_base; - - /* Grab & reserve the SMB I/O range */ - rdmsr(MSR_LBAR_SMB, low, hi); - - /* Check the IO mask and whether SMB is enabled */ - if (hi != 0x0000F001) { - printk(KERN_WARNING NAME ": SMBus not enabled\n"); - return -ENODEV; - } - - /* SMBus IO size is 8 bytes */ - smb_base = low & 0x0000FFF8; - - return scx200_acb_create("CS5535", smb_base, 0); -} - static int __init scx200_acb_init(void) { int i; - int rc = -ENODEV; + int rc; pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n"); /* Verify that this really is a SCx200 processor */ - if (pci_dev_present(scx200)) { - for (i = 0; i < MAX_DEVICES; ++i) { - if (base[i] > 0) - rc = scx200_acb_create("SCx200", base[i], i); - } - } else if (pci_dev_present(divil_pci)) - rc = scx200_add_cs553x(); + if (pci_dev_present(scx200) == 0) + return -ENODEV; - /* If at least one bus was created, init must succeed */ + rc = -ENXIO; + for (i = 0; i < MAX_DEVICES; ++i) { + if (base[i] > 0) + rc = scx200_acb_create(base[i], i); + } if (scx200_acb_list) return 0; return rc; @@ -540,19 +530,26 @@ static int __init scx200_acb_init(void) static void __exit scx200_acb_cleanup(void) { struct scx200_acb_iface *iface; - - down(&scx200_acb_list_mutex); + lock_kernel(); while ((iface = scx200_acb_list) != NULL) { scx200_acb_list = iface->next; - up(&scx200_acb_list_mutex); + unlock_kernel(); i2c_del_adapter(&iface->adapter); release_region(iface->base, 8); kfree(iface); - down(&scx200_acb_list_mutex); + lock_kernel(); } - up(&scx200_acb_list_mutex); + unlock_kernel(); } module_init(scx200_acb_init); module_exit(scx200_acb_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules" + c-basic-offset: 8 + End: +*/ +