From: "Alan Bennett" <alan@akb.net>
To: linuxppc-dev@ozlabs.org
Subject: Configuration of i2c on 8248 (cpm2) help
Date: Fri, 16 Nov 2007 11:44:37 -0700 [thread overview]
Message-ID: <bfa0697f0711161044j40eb8b8bq33e6938674e6d59c@mail.gmail.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 3108 bytes --]
I'm working to adapt some work by Jochen Friedrich to support CPM2 i2c
devices. It appears I have the bus loaded and think I am configuring
it properly, but my results tell me different. I see no messages when
I attach a i2c monitor after u-boot loads.
Can anyone spot what is going wrong based on the following information
and the resulting output? Does this look proper for a successful
configuration of the cpm2 i2c bus?
i2c-cpm: iip e0008afc, dp_addr 0x240
i2c-cpm: iic_tbase 576, iic_rbase 608
Any insight would be greatly appreciated.
-Alan
My Device tree entry:
i2c@11860 {
compatible = "fsl,mpc8248-i2c",
"fsl,cpm2-i2c",
"fsl,cpm-i2c";
reg = <11860 20 8afc 30>;
interrupts = <1 8>;
interrupt-parent = <&PIC>;
fsl,cpm-command = <29600000>;
};
... and then later:
muram@0 {
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0 10000>;
data@0 {
compatible = "fsl,cpm-muram-data";
reg = <80 1f80 9800 800>;
};
};
dump of 0xe0008afc (my immr_base=0xe0000000) at boot
e0008afc : 02600240 10100201 00000000 00000000
e0008b0c : 02600000 00000000 00000000 00000000
e0008b1c : 02400000 00000000 78c7ebaf bdefeab1
Probe debug results:
i2c-cpm: cpm_iic_init()
i2c-cpm: iip e0008afc, dp_addr 0x240
i2c-cpm: iic_tbase 576, iic_rbase 608
Log after modprobe:
modprobe i2c-dev
Jan 1 00:08:02 192 kernel: i2c /dev entries driver
Jan 1 00:08:02 192 kernel: i2c-core: driver [dev_driver] registered
Jan 1 00:08:02 192 kernel: i2c-dev: adapter [i2c-cpm] registered as minor 0
Log entries after trying an i2cset: (NOTE: sometimes it hangs doing this)
i2cset 0 0x41 0x1 0xff b
cpm_xfer:478
cpm_parse_message:329
kernel: i2c-adapter i2c-0: ioctl, cmd=0x720, arg=0xbfecaa0c
kernel: i2c-adapter i2c-0: master_xfer[0] W, addr=0x41, len2
kernel: cpm_xfer:478
kernel: i2c-adapter i2c-0: i2c-algo-cpm.o: R: 0 T: 0
kernel: cpm_parse_message:329
kernel: i2c-adapter i2c-0: cpm_iic_write(abyte=0x82)
kernel: i2c-adapter i2c-0: tx sc 0 bc00
kernel: i2c-adapter i2c-0: test ready.
kernel: i2c-adapter i2c-0: not ready.
cpm_xfer:478
cpm_parse_message:329
cpm_parse_message:329
kernel: i2c-adapter i2c-0: force_close()
kernel: i2c-adapter i2c-0: IIC read: timeout!
kernel: i2c-adapter i2c-0: ioctl, cmd=0x720, arg=0xbfecaa0c
kernel: i2c-adapter i2c-0: master_xfer[0] W, addr=0x41, len1
kernel: i2c-adapter i2c-0: master_xfer[1] R, addr=0x41, len1
kernel: cpm_xfer:478
kernel: i2c-adapter i2c-0: i2c-algo-cpm.o: R: 0 T: 0
kernel: cpm_parse_message:329
kernel: i2c-adapter i2c-0: cpm_iic_write(abyte=0x82)
kernel: i2c-adapter i2c-0: tx sc 0 9400
kernel: i2c-adapter i2c-0: i2c-algo-cpm.o: R: 0 T: 1
kernel: cpm_parse_message:329
kernel: i2c-adapter i2c-0: cpm_iic_read(abyte=0x83)
kernel: i2c-adapter i2c-0: test ready.
kernel: i2c-adapter i2c-0: not ready.
kernel: i2c-adapter i2c-0: force_close()
kernel: i2c-adapter i2c-0: IIC read: timeout!
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: i2c-cpm.c --]
[-- Type: text/x-csrc; name=i2c-cpm.c, Size: 17704 bytes --]
/*
* Freescale CPM1/CPM2 I2C interface.
* Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
*
* moved into proper i2c interface;
* Brad Parker (brad@heeltoe.com)
*
* (C) 2007 Montavista Software, Inc.
* Vitaly Bordug <vitb@kernel.crashing.org>
*
* RPX lite specific parts of the i2c interface
* Update: There actually isn't anything RPXLite-specific about this module.
* This should work for most any CPM board. The console messages have been
* changed to eliminate RPXLite references.
*
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.
*
* moved into proper i2c interface; separated out platform specific
* parts into i2c-8xx.c
* Brad Parker (brad@heeltoe.com)
*
* Parts from dbox2_i2c.c (cvs.tuxbox.org)
* (C) 2000-2001 Tmbinc, Gillem (htoa@gmx.net)
*
* (C) 2007 Montavista Software, Inc.
* Vitaly Bordug <vitb@kernel.crashing.org>
*
* Converted to of_platform_device. Renamed to i2c-cpm.c.
* (C) 2007 Jochen Friedrich <jochen@scram.de>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/stddef.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/time.h>
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <sysdev/fsl_soc.h>
#ifdef CONFIG_CPM2
#include <asm/cpm2.h>
#else
#include <asm/commproc.h>
#endif
/* Try to define this if you have an older CPU (earlier than rev D4) */
/* However, better use a GPIO based bitbang driver in this case :/ */
#undef I2C_CHIP_ERRATA
#define CPM_MAX_READ 513
#define CPM_MAXBD 4
struct cpm_i2c {
char *base;
struct of_device *ofdev;
struct i2c_adapter adap;
uint dp_addr;
int reloc;
int irq;
int cp_command;
#ifdef CONFIG_CPM2
i2c_cpm2_t __iomem *i2c;
#else
i2c8xx_t __iomem *i2c;
#endif
iic_t __iomem *iip;
wait_queue_head_t iic_wait;
struct mutex iic_mutex; /* Protects I2C CPM */
u_char *txbuf[CPM_MAXBD];
u_char *rxbuf[CPM_MAXBD];
u32 txdma[CPM_MAXBD];
u32 rxdma[CPM_MAXBD];
};
static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id)
{
struct i2c_adapter *adap;
struct cpm_i2c *cpm;
#ifdef CONFIG_CPM2
i2c_cpm2_t __iomem *i2c;
#else
i2c8xx_t __iomem *i2c;
#endif
int i;
printk ("%s:%d\n", __FUNCTION__,__LINE__);
adap = (struct i2c_adapter *) dev_id;
cpm = i2c_get_adapdata(adap);
i2c = cpm->i2c;
/* Clear interrupt.
*/
i = in_8(&i2c->i2c_i2cer);
out_8(&i2c->i2c_i2cer, i);
dev_dbg(&adap->dev, "Interrupt: %x\n", i);
/* Get 'me going again.
*/
wake_up_interruptible(&cpm->iic_wait);
return i ? IRQ_HANDLED : IRQ_NONE;
}
static void cpm_reset_iic_params(struct cpm_i2c *cpm)
{
iic_t __iomem *iip = cpm->iip;
/* Set up the IIC parameters in the parameter ram.
*/
out_be16(&iip->iic_tbase, cpm->dp_addr);
out_be16(&iip->iic_rbase, cpm->dp_addr + sizeof(cbd_t) * CPM_MAXBD);
#ifdef CONFIG_CPM2
out_8(&iip->iic_tfcr, CPMFCR_EB);
out_8(&iip->iic_rfcr, CPMFCR_EB);
#else
out_8(&iip->iic_tfcr, SMC_EB);
out_8(&iip->iic_rfcr, SMC_EB);
#endif
out_be16(&iip->iic_mrblr, CPM_MAX_READ);
out_be32(&iip->iic_rstate, 0);
out_be32(&iip->iic_rdp, 0);
out_be16(&iip->iic_rbptr, in_be16(&iip->iic_rbase));
out_be16(&iip->iic_rbc, 0);
out_be32(&iip->iic_rxtmp, 0);
out_be32(&iip->iic_tstate, 0);
out_be32(&iip->iic_tdp, 0);
out_be16(&iip->iic_tbptr, in_be16(&iip->iic_tbase));
out_be16(&iip->iic_tbc, 0);
out_be32(&iip->iic_txtmp, 0);
}
static int cpm_iic_init(struct i2c_adapter *adap)
{
struct cpm_i2c *cpm = i2c_get_adapdata(adap);
iic_t __iomem *iip = cpm->iip;
#ifdef CONFIG_CPM2
i2c_cpm2_t __iomem *i2c = cpm->i2c;
#else
i2c8xx_t __iomem *i2c = cpm->i2c;
#endif
unsigned char brg;
int ret, i, res;
#ifdef CONFIG_CPM2
u32 v;
#else
u16 v;
#endif
printk("i2c-cpm: cpm_iic_init()\n");
ret = 0;
init_waitqueue_head(&cpm->iic_wait);
mutex_init(&cpm->iic_mutex);
/* Initialize Tx/Rx parameters.
*/
cpm_reset_iic_params(cpm);
printk("i2c-cpm: iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr);
printk("i2c-cpm: iic_tbase %d, iic_rbase %d\n",
in_be16(&iip->iic_tbase), in_be16(&iip->iic_rbase));
v = cpm->cp_command | (CPM_CR_INIT_TRX << 8) | CPM_CR_FLG;
#ifdef CONFIG_CPM2
out_be32(&cpmp->cp_cpcr, v);
res = wait_event_timeout(cpm->iic_wait,
!(in_be32(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 10);
#else
out_be16(&cpmp->cp_cpcr, v);
res = wait_event_timeout(cpm->iic_wait,
!(in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 10);
#endif
if (!res)
return -EIO;
/* Select an invalid address. Just make sure we don't use loopback mode
*/
out_8(&i2c->i2c_i2add, 0xfe);
/* Make clock run at 60 kHz.
*/
brg = get_brgfreq() / (32 * 2 * 60000) - 3;
out_8(&i2c->i2c_i2brg, brg);
out_8(&i2c->i2c_i2mod, 0x00);
out_8(&i2c->i2c_i2com, 0x01); /* Master mode */
/* Disable interrupts.
*/
out_8(&i2c->i2c_i2cmr, 0);
out_8(&i2c->i2c_i2cer, 0xff);
/* Allocate TX and RX buffers */
for (i = 0; i < CPM_MAXBD; i++) {
cpm->rxbuf[i] = dma_alloc_coherent(
NULL, CPM_MAX_READ + 1, &cpm->rxdma[i], GFP_KERNEL);
if (!cpm->rxbuf[i]) {
ret = -ENOMEM;
goto out;
}
cpm->txbuf[i] = (unsigned char *)dma_alloc_coherent(
NULL, CPM_MAX_READ + 1, &cpm->txdma[i], GFP_KERNEL);
if (!cpm->txbuf[i]) {
ret = -ENOMEM;
goto out;
}
}
/* Install interrupt handler.
*/
ret = request_irq(cpm->irq, cpm_iic_interrupt, 0, "cpm_i2c", adap);
if (ret)
goto out;
return 0;
out:
for (i = 0; i < CPM_MAXBD; i++) {
if (cpm->rxbuf[i])
dma_free_coherent(NULL, CPM_MAX_READ + 1,
cpm->rxbuf[i], cpm->rxdma[i]);
if (cpm->txbuf[i])
dma_free_coherent(NULL, CPM_MAX_READ + 1,
cpm->txbuf[i], cpm->txdma[i]);
}
return ret;
}
static int cpm_iic_shutdown(struct i2c_adapter *adap)
{
struct cpm_i2c *cpm = i2c_get_adapdata(adap);
int i;
#ifdef CONFIG_CPM2
i2c_cpm2_t __iomem *i2c = cpm->i2c;
#else
i2c8xx_t __iomem *i2c = cpm->i2c;
#endif
/* Shut down IIC.
*/
out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1);
out_8(&i2c->i2c_i2cmr, 0);
out_8(&i2c->i2c_i2cer, 0xff);
for (i = 0; i < CPM_MAXBD; i++) {
if (cpm->rxbuf[i])
dma_free_coherent(NULL, CPM_MAX_READ + 1,
cpm->rxbuf[i], cpm->rxdma[i]);
if (cpm->txbuf[i])
dma_free_coherent(NULL, CPM_MAX_READ + 1,
cpm->txbuf[i], cpm->txdma[i]);
}
free_irq(cpm->irq, adap);
return 0;
}
#define BD_SC_NAK (0x0004) /* NAK - did not respond */
#define BD_SC_OV (0x0002) /* OV - receive overrun */
#define CPM_CR_CLOSE_RXBD (0x0007)
static void force_close(struct i2c_adapter *adap)
{
struct cpm_i2c *cpm = i2c_get_adapdata(adap);
#ifdef CONFIG_CPM2
i2c_cpm2_t __iomem *i2c = cpm->i2c;
u32 v;
#else
i2c8xx_t __iomem *i2c = cpm->i2c;
u16 v;
#endif
dev_dbg(&adap->dev, "force_close()\n");
v = cpm->cp_command | (CPM_CR_CLOSE_RXBD << 8) | CPM_CR_FLG;
#ifdef CONFIG_CPM2
out_be32(&cpmp->cp_cpcr, v);
wait_event_timeout(cpm->iic_wait,
!(in_be32(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 5);
#else
out_be16(&cpmp->cp_cpcr, v);
wait_event_timeout(cpm->iic_wait,
!(in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 5);
#endif
out_8(&i2c->i2c_i2cmr, 0x00); /* Disable all interrupts */
out_8(&i2c->i2c_i2cer, 0xff);
}
static void cpm_parse_message(struct i2c_adapter *adap, struct i2c_msg *pmsg,
int num, int tx, int rx)
{
cbd_t *tbdf, *rbdf;
u_char addr;
u_char *tb;
u_char *rb;
struct cpm_i2c *cpm = i2c_get_adapdata(adap);
iic_t __iomem *iip = cpm->iip;
int i, dscan;
printk ("%s:%d\n", __FUNCTION__,__LINE__);
tbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_tbase));
rbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_rbase));
/* This chip can't do zero length writes. However, the i2c core uses
them to scan for devices. The best we can do is to convert them
into 1 byte reads */
dscan = ((pmsg->len == 0) && (num == 1));
addr = pmsg->addr << 1;
if ((pmsg->flags & I2C_M_RD) || dscan)
addr |= 1;
tb = cpm->txbuf[tx];
rb = cpm->rxbuf[rx];
/* Align read buffer */
rb = (u_char *) (((ulong) rb + 1) & ~1);
if ((pmsg->flags & I2C_M_RD) || dscan) {
/* To read, we need an empty buffer of the proper length.
* All that is used is the first byte for address, the remainder
* is just used for timing (and doesn't really have to exist).
*/
tb[0] = addr; /* Device address byte w/rw flag */
dev_dbg(&adap->dev, "cpm_iic_read(abyte=0x%x)\n", addr);
tbdf[tx].cbd_bufaddr = cpm->txdma[tx];
if (dscan)
tbdf[tx].cbd_datlen = 2;
else
tbdf[tx].cbd_datlen = pmsg->len + 1;
tbdf[tx].cbd_sc = 0;
if (!(pmsg->flags & I2C_M_NOSTART))
tbdf[tx].cbd_sc |= BD_IIC_START;
if (tx + 1 == num)
tbdf[tx].cbd_sc |= BD_SC_LAST | BD_SC_WRAP;
rbdf[rx].cbd_datlen = 0;
rbdf[rx].cbd_bufaddr = ((cpm->rxdma[rx] + 1) & ~1);
rbdf[rx].cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
if (rx + 1 == CPM_MAXBD)
tbdf[rx].cbd_sc |= BD_SC_WRAP;
eieio();
tbdf[tx].cbd_sc |= BD_SC_READY;
} else {
tb[0] = addr; /* Device address byte w/rw flag */
for (i = 0; i < pmsg->len; i++)
tb[i+1] = pmsg->buf[i];
dev_dbg(&adap->dev, "cpm_iic_write(abyte=0x%x)\n", addr);
tbdf[tx].cbd_bufaddr = cpm->txdma[tx];
tbdf[tx].cbd_datlen = pmsg->len + 1;
tbdf[tx].cbd_sc = 0;
if (!(pmsg->flags & I2C_M_NOSTART))
tbdf[tx].cbd_sc |= BD_IIC_START;
if (tx + 1 == num)
tbdf[tx].cbd_sc |= BD_SC_LAST | BD_SC_WRAP;
eieio();
tbdf[tx].cbd_sc |= BD_SC_READY | BD_SC_INTRPT;
dev_dbg(&adap->dev, "tx sc %d %04x\n",
tx, tbdf[tx].cbd_sc);
}
}
static int cpm_check_message(struct i2c_adapter *adap, struct i2c_msg *pmsg,
int tx, int rx)
{
cbd_t *tbdf, *rbdf;
u_char *tb;
u_char *rb;
struct cpm_i2c *cpm = i2c_get_adapdata(adap);
iic_t __iomem *iip = cpm->iip;
int i;
printk ("%s:%d\n", __FUNCTION__,__LINE__);
tbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_tbase));
rbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_rbase));
tb = cpm->txbuf[tx];
rb = cpm->rxbuf[rx];
/* Align read buffer */
rb = (u_char *) (((uint) rb + 1) & ~1);
if (pmsg->flags & I2C_M_RD) {
dev_dbg(&adap->dev, "rx sc %04x, rx sc %04x\n",
tbdf[tx].cbd_sc, rbdf[rx].cbd_sc);
if (tbdf[tx].cbd_sc & BD_SC_NAK) {
dev_dbg(&adap->dev, "IIC read; no ack\n");
if (pmsg->flags & I2C_M_IGNORE_NAK)
return 0;
else
return -EIO;
}
if (rbdf[rx].cbd_sc & BD_SC_EMPTY) {
dev_dbg(&adap->dev,
"IIC read; complete but rbuf empty\n");
return -EREMOTEIO;
}
if (rbdf[rx].cbd_sc & BD_SC_OV) {
dev_dbg(&adap->dev, "IIC read; Overrun\n");
return -EREMOTEIO;
}
for (i = 0; i < pmsg->len; i++)
pmsg->buf[i] = rb[i];
} else {
dev_dbg(&adap->dev, "tx sc %d %04x\n", tx, tbdf[tx].cbd_sc);
if (tbdf[tx].cbd_sc & BD_SC_NAK) {
dev_dbg(&adap->dev, "IIC write; no ack\n");
if (pmsg->flags & I2C_M_IGNORE_NAK)
return 0;
else
return -EIO;
}
}
return 0;
}
static int cpm_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct cpm_i2c *cpm = i2c_get_adapdata(adap);
#ifdef CONFIG_CPM2
i2c_cpm2_t __iomem *i2c = cpm->i2c;
#else
i2c8xx_t __iomem *i2c = cpm->i2c;
#endif
iic_t __iomem *iip = cpm->iip;
struct i2c_msg *pmsg, *rmsg;
int ret, i;
int tptr;
int rptr;
cbd_t *tbdf, *rbdf;
printk ("%s:%d\n", __FUNCTION__,__LINE__);
if (num > CPM_MAXBD)
return -EINVAL;
/* Check if we have any oversized READ requests */
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
if (pmsg->len >= CPM_MAX_READ)
return -EINVAL;
}
mutex_lock(&cpm->iic_mutex);
/* Reset to use first buffer */
out_be16(&iip->iic_rbptr, in_be16(&iip->iic_rbase));
out_be16(&iip->iic_tbptr, in_be16(&iip->iic_tbase));
tbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_tbase));
rbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_rbase));
tptr = 0;
rptr = 0;
while (tptr < num) {
pmsg = &msgs[tptr];
dev_dbg(&adap->dev, "i2c-algo-cpm.o: " "R: %d T: %d\n",
rptr, tptr);
cpm_parse_message(adap, pmsg, num, tptr, rptr);
if (pmsg->flags & I2C_M_RD)
rptr++;
tptr++;
}
/* Start transfer now */
/* Chip bug, set enable here */
out_8(&i2c->i2c_i2cmr, 0x13); /* Enable some interupts */
out_8(&i2c->i2c_i2cer, 0xff);
out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | 1); /* Enable */
/* Begin transmission */
out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) | 0x80);
tptr = 0;
rptr = 0;
while (tptr < num) {
/* Check for outstanding messages */
dev_dbg(&adap->dev, "test ready.\n");
if (!(tbdf[tptr].cbd_sc & BD_SC_READY)) {
dev_dbg(&adap->dev, "ready.\n");
rmsg = &msgs[tptr];
ret = cpm_check_message(adap, rmsg, tptr, rptr);
tptr++;
if (rmsg->flags & I2C_M_RD)
rptr++;
if (ret) {
force_close(adap);
mutex_unlock(&cpm->iic_mutex);
return ret;
}
} else {
dev_dbg(&adap->dev, "not ready.\n");
ret = wait_event_interruptible_timeout(cpm->iic_wait,
!(tbdf[tptr].cbd_sc & BD_SC_READY), 1 * HZ);
if (ret == 0) {
force_close(adap);
dev_dbg(&adap->dev, "IIC read: timeout!\n");
mutex_unlock(&cpm->iic_mutex);
return -EREMOTEIO;
}
}
}
#ifdef I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
Disabling I2C too early may cause too short stop condition */
udelay(4);
out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1);
#endif
mutex_unlock(&cpm->iic_mutex);
return (num);
}
static u32 cpm_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
/* -----exported algorithm data: ------------------------------------- */
static struct i2c_algorithm cpm_algo = {
.master_xfer = cpm_xfer,
.functionality = cpm_func,
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_cpm_add_bus(struct i2c_adapter *adap)
{
int res;
printk("i2c-cpm: hw routines for %s registered.\n", adap->name);
/* register new adapter to i2c module... */
adap->algo = &cpm_algo;
res = cpm_iic_init(adap);
if (res)
return res;
return i2c_add_adapter(adap);
}
int i2c_cpm_del_bus(struct i2c_adapter *adap)
{
i2c_del_adapter(adap);
return cpm_iic_shutdown(adap);
}
static const struct i2c_adapter cpm_ops = {
.owner = THIS_MODULE,
.name = "i2c-cpm",
.id = I2C_HW_MPC8XX_EPON,
.class = I2C_CLASS_HWMON,
};
static int i2c_cpm_setup(struct cpm_i2c *i2c)
{
struct of_device *ofdev = i2c->ofdev;
const u32 *data;
int len;
/* Pointer to Communication Processor
*/
i2c->irq = of_irq_to_resource(ofdev->node, 0, NULL);
if (i2c->irq == NO_IRQ)
return -EINVAL;
i2c->iip = of_iomap(ofdev->node, 1);
if (i2c->iip == NULL)
return -EINVAL;
#ifndef CONFIG_CPM2
/* Check for and use a microcode relocation patch.
*/
if (of_device_is_compatible(ofdev->node, "fsl,i2c-cpm1"))
i2c->reloc = i2c->iip->iic_rpbase;
/* Maybe should use ioremap instead?
*/
if (i2c->reloc) {
iounmap(i2c->iip);
i2c->iip = cpm_muram_addr(i2c->iip->iic_rpbase);
}
#endif
i2c->i2c = of_iomap(ofdev->node, 0);
if (i2c->i2c == NULL)
return -EINVAL;
/* Allocate space for two transmit and two receive buffer
* descriptors in the DP ram.
*/
i2c->dp_addr = cpm_muram_alloc(sizeof(cbd_t) * 4, 8);
if (!i2c->dp_addr)
return -ENOMEM;
data = of_get_property(ofdev->node, "fsl,cpm-command", &len);
if (!data || len != 4)
return -EINVAL;
i2c->cp_command = *data;
return 0;
}
static void i2c_cpm_release(struct cpm_i2c *i2c)
{
if (i2c->dp_addr)
cpm_muram_free(i2c->dp_addr);
if (i2c->i2c)
iounmap(i2c->i2c);
if ((i2c->iip) && (!i2c->reloc))
iounmap(i2c->iip);
return;
}
static int i2c_cpm_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
int result;
struct cpm_i2c *i2c;
i2c = kzalloc(sizeof(struct cpm_i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
i2c->ofdev = ofdev;
result = i2c_cpm_setup(i2c);
if (result) {
printk(KERN_ERR "i2c-cpm: Unable to register resources\n");
goto out;
}
dev_set_drvdata(&ofdev->dev, i2c);
i2c->adap = cpm_ops;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &ofdev->dev;
result = i2c_cpm_add_bus(&i2c->adap);
if (result < 0) {
printk(KERN_ERR "i2c-cpm: Unable to register with I2C\n");
goto out;
}
return 0;
out:
i2c_cpm_release(i2c);
kfree(i2c);
return result;
}
static int i2c_cpm_remove(struct of_device *ofdev)
{
struct cpm_i2c *i2c = dev_get_drvdata(&ofdev->dev);
i2c_cpm_del_bus(&i2c->adap);
dev_set_drvdata(&ofdev->dev, NULL);
i2c_cpm_release(i2c);
kfree(i2c);
return 0;
}
static struct of_device_id i2c_cpm_match[] = {
{
.compatible = "fsl,cpm-i2c",
},
{},
};
MODULE_DEVICE_TABLE(of, i2c_cpm_match);
static struct of_platform_driver i2c_cpm_driver = {
.name = "fsl-i2c-cpm",
.match_table = i2c_cpm_match,
.probe = i2c_cpm_probe,
.remove = i2c_cpm_remove,
};
static int __init i2c_cpm_init(void)
{
return of_register_platform_driver(&i2c_cpm_driver);
}
static void __exit i2c_cpm_exit(void)
{
of_unregister_platform_driver(&i2c_cpm_driver);
}
module_init(i2c_cpm_init);
module_exit(i2c_cpm_exit);
MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for CPM boards");
MODULE_LICENSE("GPL");
reply other threads:[~2007-11-16 18:44 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=bfa0697f0711161044j40eb8b8bq33e6938674e6d59c@mail.gmail.com \
--to=alan@akb.net \
--cc=linuxppc-dev@ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).