/*
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include "ipath_kernel.h"
/*
* InfiniPath I2C driver for a serial eeprom. This is not a generic
* I2C interface. For a start, the device we're using (Atmel AT24C11)
* doesn't work like a regular I2C device. It looks like one
* electrically, but not logically. Normal I2C devices have a single
* 7-bit or 10-bit I2C address that they respond to. Valid 7-bit
* addresses range from 0x03 to 0x77. Addresses 0x00 to 0x02 and 0x78
* to 0x7F are special reserved addresses (e.g. 0x00 is the "general
* call" address.) The Atmel device, on the other hand, responds to ALL
* 7-bit addresses. It's designed to be the only device on a given I2C
* bus. A 7-bit address corresponds to the memory address within the
* Atmel device itself.
*
* Also, the timing requirements mean more than simple software
* bitbanging, with readbacks from chip to ensure timing (simple udelay
* is not enough).
*
* This all means that accessing the device is specialized enough
* that using the standard kernel I2C bitbanging interface would be
* impossible. For example, the core I2C eeprom driver expects to find
* a device at one or more of a limited set of addresses only. It doesn't
* allow writing to an eeprom. It also doesn't provide any means of
* accessing eeprom contents from within the kernel, only via sysfs.
*/
/* Added functionality for IBA7220-based cards */
#define IPATH_EEPROM_DEV_V1 0xA0
#define IPATH_EEPROM_DEV_V2 0xA2
#define IPATH_TEMP_DEV 0x98
#define IPATH_BAD_DEV (IPATH_EEPROM_DEV_V2+2)
#define IPATH_NO_DEV (0xFF)
/*
* The number of I2C chains is proliferating. Table below brings
* some order to the madness. The basic principle is that the
* table is scanned from the top, and a "probe" is made to the
* device probe_dev. If that succeeds, the chain is considered
* to be of that type, and dd->i2c_chain_type is set to the index+1
* of the entry.
* The +1 is so static initialization can mean "unknown, do probe."
*/
static struct i2c_chain_desc {
u8 probe_dev; /* If seen at probe, chain is this type */
u8 eeprom_dev; /* Dev addr (if any) for EEPROM */
u8 temp_dev; /* Dev Addr (if any) for Temp-sense */
} i2c_chains[] = {
{ IPATH_BAD_DEV, IPATH_NO_DEV, IPATH_NO_DEV }, /* pre-iba7220 bds */
{ IPATH_EEPROM_DEV_V1, IPATH_EEPROM_DEV_V1, IPATH_TEMP_DEV}, /* V1 */
{ IPATH_EEPROM_DEV_V2, IPATH_EEPROM_DEV_V2, IPATH_TEMP_DEV}, /* V2 */
{ IPATH_NO_DEV }
};
enum i2c_type {
i2c_line_scl = 0,
i2c_line_sda
};
enum i2c_state {
i2c_line_low = 0,
i2c_line_high
};
#define READ_CMD 1
#define WRITE_CMD 0
/**
* i2c_gpio_set - set a GPIO line
* @dd: the infinipath device
* @line: the line to set
* @new_line_state: the state to set
*
* Returns 0 if the line was set to the new state successfully, non-zero
* on error.
*/
static int i2c_gpio_set(struct ipath_devdata *dd,
enum i2c_type line,
enum i2c_state new_line_state)
{
u64 out_mask, dir_mask, *gpioval;
unsigned long flags = 0;
gpioval = &dd->ipath_gpio_out;
if (line == i2c_line_scl) {
dir_mask = dd->ipath_gpio_scl;
out_mask = (1UL << dd->ipath_gpio_scl_num);
} else {
dir_mask = dd->ipath_gpio_sda;
out_mask = (1UL << dd->ipath_gpio_sda_num);
}
spin_lock_irqsave(&dd->ipath_gpio_lock, flags);
if (new_line_state == i2c_line_high) {
/* tri-state the output rather than force high */
dd->ipath_extctrl &= ~dir_mask;
} else {
/* config line to be an output */
dd->ipath_extctrl |= dir_mask;
}
ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, dd->ipath_extctrl);
/* set output as well (no real verify) */
if (new_line_state == i2c_line_high)
*gpioval |= out_mask;
else
*gpioval &= ~out_mask;
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval);
spin_unlock_irqrestore