/* i2c-core.c - a device driver for the iic-bus interface */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 1995-99 Simon G. Vogl
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. */
/* ------------------------------------------------------------------------- */
/* SHARK version by Giacomo Guidi <giacomo@gandalf.sssup.it> */
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <kernel/log.h>
#include <asm/errno.h>
#include "drivers/i2c.h"
#define DEB(x) if (i2c_debug>=1) x;
#define DEB2(x) if (i2c_debug>=2) x;
/* ----- global variables -------------------------------------------------- */
/**** adapter list */
static struct i2c_adapter
*adapters
[I2C_ADAP_MAX
];
static int adap_count
;
/**** drivers list */
static struct i2c_driver
*drivers
[I2C_DRIVER_MAX
];
static int driver_count
;
/**** debug level */
static int i2c_debug
= 0;
#define i2cproc_init() 0
#define i2cproc_cleanup() 0
/* ---------------------------------------------------
* registering functions
* ---------------------------------------------------
*/
/* -----
* i2c_add_adapter is called from within the algorithm layer,
* when a new hw adapter registers. A new device is register to be
* available for clients.
*/
int i2c_add_adapter
(struct i2c_adapter
*adap
)
{
int i
,j
,res
;
for (i
= 0; i
< I2C_ADAP_MAX
; i
++)
if (NULL
== adapters
[i
])
break;
if (I2C_ADAP_MAX
== i
) {
printk
(KERN_INFO
"i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", adap
->name
);
res
= -ENOMEM
;
goto ERROR0
;
}
adapters
[i
] = adap
;
adap_count
++;
/* inform drivers of new adapters */
for (j
=0;j
<I2C_DRIVER_MAX
;j
++)
if (drivers
[j
]!=NULL
&&
(drivers
[j
]->flags
&(I2C_DF_NOTIFY
|I2C_DF_DUMMY
)))
/* We ignore the return code; if it fails, too bad */
drivers
[j
]->attach_adapter
(adap
);
printk
(KERN_INFO
"i2c-core.o: adapter %s registered as adapter %d.\n", adap
->name
,i
);
return 0;
ERROR0
:
return res
;
}
int i2c_del_adapter
(struct i2c_adapter
*adap
)
{
int i
,j
,res
;
for (i
= 0; i
< I2C_ADAP_MAX
; i
++)
if (adap
== adapters
[i
])
break;
if (I2C_ADAP_MAX
== i
) {
printk
(KERN_INFO
"i2c-core.o: unregister_adapter adap [%s] not found.\n",
adap
->name
);
res
= -ENODEV
;
goto ERROR0
;
}
/* DUMMY drivers do not register their clients, so we have to
* use a trick here: we call driver->attach_adapter to
* *detach* it! Of course, each dummy driver should know about
* this or hell will break loose...
*/
for (j
= 0; j
< I2C_DRIVER_MAX
; j
++)
if (drivers
[j
] && (drivers
[j
]->flags
& I2C_DF_DUMMY
))
if ((res
= drivers
[j
]->attach_adapter
(adap
))) {
cprintf
("i2c-core.o: can't detach adapter %s "
"while detaching driver %s: driver not "
"detached!",adap
->name
,drivers
[j
]->name
);
goto ERROR1
;
}
/* detach any active clients. This must be done first, because
* it can fail; in which case we give upp. */
for (j
=0;j
<I2C_CLIENT_MAX
;j
++) {
struct i2c_client
*client
= adap
->clients
[j
];
if (client
!=NULL
)
/* detaching devices is unconditional of the set notify
* flag, as _all_ clients that reside on the adapter
* must be deleted, as this would cause invalid states.
*/
if ((res
=client
->driver
->detach_client
(client
))) {
cprintf
("i2c-core.o: adapter %s not "
"unregistered, because client at "
"address %02x can't be detached. ",
adap
->name
, client
->addr
);
goto ERROR0
;
}
}
adapters
[i
] = NULL
;
adap_count
--;
DEB
(cprintf
("i2c-core.o: adapter unregistered: %s\n",adap
->name
));
return 0;
ERROR0
:
return res
;
ERROR1
:
return res
;
}
/* -----
* What follows is the "upwards" interface: commands for talking to clients,
* which implement the functions to access the physical information of the
* chips.
*/
int i2c_add_driver
(struct i2c_driver
*driver
)
{
int i
;
for (i
= 0; i
< I2C_DRIVER_MAX
; i
++)
if (NULL
== drivers
[i
])
break;
if (I2C_DRIVER_MAX
== i
) {
printk
(KERN_INFO
"i2c-core.o: register_driver(%s) "
"- enlarge I2C_DRIVER_MAX.\n",
driver
->name
);
return -ENOMEM
;
}
drivers
[i
] = driver
;
driver_count
++;
DEB
(cprintf
("i2c-core.o: driver %s registered.\n",driver
->name
));
/* now look for instances of driver on our adapters
*/
if (driver
->flags
& (I2C_DF_NOTIFY
|I2C_DF_DUMMY
)) {
for (i
=0;i
<I2C_ADAP_MAX
;i
++)
if (adapters
[i
]!=NULL
)
/* Ignore errors */
driver
->attach_adapter
(adapters
[i
]);
}
return 0;
}
int i2c_del_driver
(struct i2c_driver
*driver
)
{
int i
,j
,k
,res
;
for (i
= 0; i
< I2C_DRIVER_MAX
; i
++)
if (driver
== drivers
[i
])
break;
if (I2C_DRIVER_MAX
== i
) {
printk
(KERN_INFO
"i2c-core.o: unregister_driver: "
"[%s] not found\n",
driver
->name
);
return -ENODEV
;
}
/* Have a look at each adapter, if clients of this driver are still
* attached. If so, detach them to be able to kill the driver
* afterwards.
*/
DEB2
(cprintf
("i2c-core.o: unregister_driver - looking for clients.\n"));
/* removing clients does not depend on the notify flag, else
* invalid operation might (will!) result, when using stale client
* pointers.
*/
for (k
=0;k
<I2C_ADAP_MAX
;k
++) {
struct i2c_adapter
*adap
= adapters
[k
];
if (adap
== NULL
) /* skip empty entries. */
continue;
DEB2
(cprintf
("i2c-core.o: examining adapter %s:\n",
adap
->name
));
if (driver
->flags
& I2C_DF_DUMMY
) {
/* DUMMY drivers do not register their clients, so we have to
* use a trick here: we call driver->attach_adapter to
* *detach* it! Of course, each dummy driver should know about
* this or hell will break loose...
*/
if ((res
= driver
->attach_adapter
(adap
))) {
cprintf
("i2c-core.o: while unregistering "
"dummy driver %s, adapter %s could "
"not be detached properly; driver "
"not unloaded!",driver
->name
,
adap
->name
);
return res
;
}
} else {
for (j
=0;j
<I2C_CLIENT_MAX
;j
++) {
struct i2c_client
*client
= adap
->clients
[j
];
if (client
!= NULL
&&
client
->driver
== driver
) {
DEB2
(cprintf
("i2c-core.o: "
"detaching client %s:\n",
client
->name
));
if ((res
= driver
->
detach_client
(client
)))
{
cprintf
("i2c-core.o: while "
"unregistering driver "
"`%s', the client at "
"address %02x of "
"adapter `%s' could not"
"be detached; driver"
"not unloaded!",
driver
->name
,
client
->addr
,
adap
->name
);
return res
;
}
}
}
}
}
drivers
[i
] = NULL
;
driver_count
--;
DEB
(cprintf
("i2c-core.o: driver unregistered: %s\n",driver
->name
));
return 0;
}
int i2c_check_addr
(struct i2c_adapter
*adapter
, int addr
)
{
int i
;
for (i
= 0; i
< I2C_CLIENT_MAX
; i
++)
if (adapter
->clients
[i
] && (adapter
->clients
[i
]->addr
== addr
))
return -EBUSY
;
return 0;
}
int i2c_attach_client
(struct i2c_client
*client
)
{
struct i2c_adapter
*adapter
= client
->adapter
;
int i
;
if (i2c_check_addr
(client
->adapter
,client
->addr
))
return -EBUSY
;
for (i
= 0; i
< I2C_CLIENT_MAX
; i
++)
if (NULL
== adapter
->clients
[i
])
break;
if (I2C_CLIENT_MAX
== i
) {
printk
(KERN_INFO
"i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n",
client
->name
);
return -ENOMEM
;
}
adapter
->clients
[i
] = client
;
adapter
->client_count
++;
if (adapter
->client_register
)
if (adapter
->client_register
(client
))
cprintf
("i2c-core.o: warning: client_register seems "
"to have failed for client %02x at adapter %s\n",
client
->addr
,adapter
->name
);
DEB
(cprintf
("i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n",
client
->name
, adapter
->name
,i
));
if(client
->flags
& I2C_CLIENT_ALLOW_USE
)
client
->usage_count
= 0;
return 0;
}
int i2c_detach_client
(struct i2c_client
*client
)
{
struct i2c_adapter
*adapter
= client
->adapter
;
int i
,res
;
for (i
= 0; i
< I2C_CLIENT_MAX
; i
++)
if (client
== adapter
->clients
[i
])
break;
if (I2C_CLIENT_MAX
== i
) {
cprintf
("i2c-core.o: unregister_client "
"[%s] not found\n",
client
->name
);
return -ENODEV
;
}
if( (client
->flags
& I2C_CLIENT_ALLOW_USE
) &&
(client
->usage_count
>0))
return -EBUSY
;
if (adapter
->client_unregister
!= NULL
)
if ((res
= adapter
->client_unregister
(client
))) {
cprintf
("i2c-core.o: client_unregister [%s] failed, "
"client not detached",client
->name
);
return res
;
}
adapter
->clients
[i
] = NULL
;
adapter
->client_count
--;
DEB
(cprintf
("i2c-core.o: client [%s] unregistered.\n",client
->name
));
return 0;
}
void i2c_inc_use_client
(struct i2c_client
*client
)
{
if (client
->driver
->inc_use
!= NULL
)
client
->driver
->inc_use
(client
);
if (client
->adapter
->inc_use
!= NULL
)
client
->adapter
->inc_use
(client
->adapter
);
}
void i2c_dec_use_client
(struct i2c_client
*client
)
{
if (client
->driver
->dec_use
!= NULL
)
client
->driver
->dec_use
(client
);
if (client
->adapter
->dec_use
!= NULL
)
client
->adapter
->dec_use
(client
->adapter
);
}
struct i2c_client
*i2c_get_client
(int driver_id
, int adapter_id
,
struct i2c_client
*prev
)
{
int i
,j
;
/* Will iterate through the list of clients in each adapter of adapters-list
in search for a client that matches the search criteria. driver_id or
adapter_id are ignored if set to 0. If both are ignored this returns
first client found. */
i
= j
= 0;
/* set starting point */
if(prev
)
{
if(!(prev
->adapter
))
return (struct i2c_client
*) -EINVAL
;
for(j
=0; j
< I2C_ADAP_MAX
; j
++)
if(prev
->adapter
== adapters
[j
])
break;
/* invalid starting point? */
if (I2C_ADAP_MAX
== j
) {
printk
(KERN_INFO
"i2c-core.o: get_client adapter for client:[%s] not found\n",
prev
->name
);
return (struct i2c_client
*) -ENODEV
;
}
for(i
=0; i
< I2C_CLIENT_MAX
; i
++)
if(prev
== adapters
[j
]->clients
[i
])
break;
/* invalid starting point? */
if (I2C_CLIENT_MAX
== i
) {
printk
(KERN_INFO
"i2c-core.o: get_client client:[%s] not found\n",
prev
->name
);
return (struct i2c_client
*) -ENODEV
;
}
i
++; /* start from one after prev */
}
for(; j
< I2C_ADAP_MAX
; j
++)
{
if(!adapters
[j
])
continue;
if(adapter_id
&& (adapters
[j
]->id
!= adapter_id
))
continue;
for(; i
< I2C_CLIENT_MAX
; i
++)
{
if(!adapters
[j
]->clients
[i
])
continue;
if(driver_id
&& (adapters
[j
]->clients
[i
]->driver
->id
!= driver_id
))
continue;
if(adapters
[j
]->clients
[i
]->flags
& I2C_CLIENT_ALLOW_USE
)
return adapters
[j
]->clients
[i
];
}
i
= 0;
}
return 0;
}
int i2c_use_client
(struct i2c_client
*client
)
{
if(client
->flags
& I2C_CLIENT_ALLOW_USE
) {
if (client
->flags
& I2C_CLIENT_ALLOW_MULTIPLE_USE
)
client
->usage_count
++;
else {
if(client
->usage_count
> 0)
return -EBUSY
;
else
client
->usage_count
++;
}
}
i2c_inc_use_client
(client
);
return 0;
}
int i2c_release_client
(struct i2c_client
*client
)
{
if(client
->flags
& I2C_CLIENT_ALLOW_USE
) {
if(client
->usage_count
>0)
client
->usage_count
--;
else
{
cprintf
("i2c-core.o: dec_use_client used one too many times\n");
return -EPERM
;
}
}
i2c_dec_use_client
(client
);
return 0;
}
/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
*/
int i2c_transfer
(struct i2c_adapter
* adap
, struct i2c_msg msgs
[],int num
)
{
int ret
;
if (adap
->algo
->master_xfer
) {
DEB2
(cprintf
("i2c-core.o: master_xfer: %s with %d msgs.\n",
adap
->name
,num
));
ret
= adap
->algo
->master_xfer
(adap
,msgs
,num
);
return ret
;
} else {
printk
(KERN_INFO
"i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n",
adap
->id
);
return -ENOSYS
;
}
}
int i2c_master_send
(struct i2c_client
*client
,const char *buf
,int count
)
{
int ret
;
struct i2c_adapter
*adap
=client
->adapter
;
struct i2c_msg msg
;
if (client
->adapter
->algo
->master_xfer
) {
msg.
addr = client
->addr
;
msg.
flags = client
->flags
& I2C_M_TEN
;
msg.
len = count
;
(const char *)msg.
buf = buf
;
DEB2
(cprintf
("i2c-core.o: master_send: writing %d bytes on %s.\n",
count
,client
->adapter
->name
));
ret
= adap
->algo
->master_xfer
(adap
,&msg
,1);
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret
== 1 )? count
: ret
;
} else {
printk
(KERN_INFO
"i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n",
client
->adapter
->id
);
return -ENOSYS
;
}
}
int i2c_master_recv
(struct i2c_client
*client
, char *buf
,int count
)
{
struct i2c_adapter
*adap
=client
->adapter
;
struct i2c_msg msg
;
int ret
;
if (client
->adapter
->algo
->master_xfer
) {
msg.
addr = client
->addr
;
msg.
flags = client
->flags
& I2C_M_TEN
;
msg.
flags |= I2C_M_RD
;
msg.
len = count
;
msg.
buf = buf
;
DEB2
(cprintf
("i2c-core.o: master_recv: reading %d bytes on %s.\n",
count
,client
->adapter
->name
));
ret
= adap
->algo
->master_xfer
(adap
,&msg
,1);
DEB2
(cprintf
("i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n",
ret
, count
, client
->addr
));
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret
== 1 )? count
: ret
;
} else {
printk
(KERN_INFO
"i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n",
client
->adapter
->id
);
return -ENOSYS
;
}
}
int i2c_control
(struct i2c_client
*client
,
unsigned int cmd
, unsigned long arg
)
{
int ret
= 0;
struct i2c_adapter
*adap
= client
->adapter
;
DEB2
(cprintf
("i2c-core.o: i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd
, arg
));
switch ( cmd
) {
case I2C_RETRIES
:
adap
->retries
= arg
;
break;
case I2C_TIMEOUT
:
adap
->timeout
= arg
;
break;
default:
if (adap
->algo
->algo_control
!=NULL
)
ret
= adap
->algo
->algo_control
(adap
,cmd
,arg
);
}
return ret
;
}
/* ----------------------------------------------------
* the i2c address scanning function
* Will not work for 10-bit addresses!
* ----------------------------------------------------
*/
int i2c_probe
(struct i2c_adapter
*adapter
,
struct i2c_client_address_data
*address_data
,
i2c_client_found_addr_proc
*found_proc
)
{
int addr
,i
,found
,err
;
int adap_id
= i2c_adapter_id
(adapter
);
/* Forget it if we can't probe using SMBUS_QUICK */
if (! i2c_check_functionality
(adapter
,I2C_FUNC_SMBUS_QUICK
))
return -1;
for (addr
= 0x00; addr
<= 0x7f; addr
++) {
/* Skip if already in use */
if (i2c_check_addr
(adapter
,addr
))
continue;
/* If it is in one of the force entries, we don't do any detection
at all */
found
= 0;
for (i
= 0; !found
&& (address_data
->force
[i
] != I2C_CLIENT_END
); i
+= 3) {
if (((adap_id
== address_data
->force
[i
]) ||
(address_data
->force
[i
] == ANY_I2C_BUS
)) &&
(addr
== address_data
->force
[i
+1])) {
DEB2
(cprintf
("i2c-core.o: found force parameter for adapter %d, addr %04x\n",
adap_id
,addr
));
if ((err
= found_proc
(adapter
,addr
,0,0)))
return err
;
found
= 1;
}
}
if (found
)
continue;
/* If this address is in one of the ignores, we can forget about
it right now */
for (i
= 0;
!found
&& (address_data
->ignore
[i
] != I2C_CLIENT_END
);
i
+= 2) {
if (((adap_id
== address_data
->ignore
[i
]) ||
((address_data
->ignore
[i
] == ANY_I2C_BUS
))) &&
(addr
== address_data
->ignore
[i
+1])) {
DEB2
(cprintf
("i2c-core.o: found ignore parameter for adapter %d, "
"addr %04x\n", adap_id
,addr
));
found
= 1;
}
}
for (i
= 0;
!found
&& (address_data
->ignore_range
[i
] != I2C_CLIENT_END
);
i
+= 3) {
if (((adap_id
== address_data
->ignore_range
[i
]) ||
((address_data
->ignore_range
[i
]==ANY_I2C_BUS
))) &&
(addr
>= address_data
->ignore_range
[i
+1]) &&
(addr
<= address_data
->ignore_range
[i
+2])) {
DEB2
(cprintf
("i2c-core.o: found ignore_range parameter for adapter %d, "
"addr %04x\n", adap_id
,addr
));
found
= 1;
}
}
if (found
)
continue;
/* Now, we will do a detection, but only if it is in the normal or
probe entries */
for (i
= 0;
!found
&& (address_data
->normal_i2c
[i
] != I2C_CLIENT_END
);
i
+= 1) {
if (addr
== address_data
->normal_i2c
[i
]) {
found
= 1;
DEB2
(cprintf
("i2c-core.o: found normal i2c entry for adapter %d, "
"addr %02x", adap_id
,addr
));
}
}
for (i
= 0;
!found
&& (address_data
->normal_i2c_range
[i
] != I2C_CLIENT_END
);
i
+= 2) {
if ((addr
>= address_data
->normal_i2c_range
[i
]) &&
(addr
<= address_data
->normal_i2c_range
[i
+1])) {
found
= 1;
DEB2
(cprintf
("i2c-core.o: found normal i2c_range entry for adapter %d, "
"addr %04x\n", adap_id
,addr
));
}
}
for (i
= 0;
!found
&& (address_data
->probe
[i
] != I2C_CLIENT_END
);
i
+= 2) {
if (((adap_id
== address_data
->probe
[i
]) ||
((address_data
->probe
[i
] == ANY_I2C_BUS
))) &&
(addr
== address_data
->probe
[i
+1])) {
found
= 1;
DEB2
(cprintf
("i2c-core.o: found probe parameter for adapter %d, "
"addr %04x\n", adap_id
,addr
));
}
}
for (i
= 0;
!found
&& (address_data
->probe_range
[i
] != I2C_CLIENT_END
);
i
+= 3) {
if (((adap_id
== address_data
->probe_range
[i
]) ||
(address_data
->probe_range
[i
] == ANY_I2C_BUS
)) &&
(addr
>= address_data
->probe_range
[i
+1]) &&
(addr
<= address_data
->probe_range
[i
+2])) {
found
= 1;
DEB2
(cprintf
("i2c-core.o: found probe_range parameter for adapter %d, "
"addr %04x\n", adap_id
,addr
));
}
}
if (!found
)
continue;
/* OK, so we really should examine this address. First check
whether there is some client here at all! */
if (i2c_smbus_xfer
(adapter
,addr
,0,0,0,I2C_SMBUS_QUICK
,NULL
) >= 0)
if ((err
= found_proc
(adapter
,addr
,0,-1)))
return err
;
}
return 0;
}
/*
* return id number for a specific adapter
*/
int i2c_adapter_id
(struct i2c_adapter
*adap
)
{
int i
;
for (i
= 0; i
< I2C_ADAP_MAX
; i
++)
if (adap
== adapters
[i
])
return i
;
return -1;
}
/* The SMBus parts */
extern s32 i2c_smbus_write_quick
(struct i2c_client
* client
, u8 value
)
{
return i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
value
,0,I2C_SMBUS_QUICK
,NULL
);
}
extern s32 i2c_smbus_read_byte
(struct i2c_client
* client
)
{
union i2c_smbus_data data
;
if (i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_READ
,0,I2C_SMBUS_BYTE
, &data
))
return -1;
else
return 0x0FF & data.
byte;
}
extern s32 i2c_smbus_write_byte
(struct i2c_client
* client
, u8 value
)
{
return i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_WRITE
,value
, I2C_SMBUS_BYTE
,NULL
);
}
extern s32 i2c_smbus_read_byte_data
(struct i2c_client
* client
, u8 command
)
{
union i2c_smbus_data data
;
if (i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_READ
,command
, I2C_SMBUS_BYTE_DATA
,&data
))
return -1;
else
return 0x0FF & data.
byte;
}
extern s32 i2c_smbus_write_byte_data
(struct i2c_client
* client
, u8 command
,
u8 value
)
{
union i2c_smbus_data data
;
data.
byte = value
;
return i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_WRITE
,command
,
I2C_SMBUS_BYTE_DATA
,&data
);
}
extern s32 i2c_smbus_read_word_data
(struct i2c_client
* client
, u8 command
)
{
union i2c_smbus_data data
;
if (i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_READ
,command
, I2C_SMBUS_WORD_DATA
, &data
))
return -1;
else
return 0x0FFFF & data.
word;
}
extern s32 i2c_smbus_write_word_data
(struct i2c_client
* client
,
u8 command
, u16 value
)
{
union i2c_smbus_data data
;
data.
word = value
;
return i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_WRITE
,command
,
I2C_SMBUS_WORD_DATA
,&data
);
}
extern s32 i2c_smbus_process_call
(struct i2c_client
* client
,
u8 command
, u16 value
)
{
union i2c_smbus_data data
;
data.
word = value
;
if (i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_WRITE
,command
,
I2C_SMBUS_PROC_CALL
, &data
))
return -1;
else
return 0x0FFFF & data.
word;
}
/* Returns the number of read bytes */
extern s32 i2c_smbus_read_block_data
(struct i2c_client
* client
,
u8 command
, u8
*values
)
{
union i2c_smbus_data data
;
int i
;
if (i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_READ
,command
,
I2C_SMBUS_BLOCK_DATA
,&data
))
return -1;
else {
for (i
= 1; i
<= data.
block[0]; i
++)
values
[i
-1] = data.
block[i
];
return data.
block[0];
}
}
extern s32 i2c_smbus_write_block_data
(struct i2c_client
* client
,
u8 command
, u8 length
, u8
*values
)
{
union i2c_smbus_data data
;
int i
;
if (length
> 32)
length
= 32;
for (i
= 1; i
<= length
; i
++)
data.
block[i
] = values
[i
-1];
data.
block[0] = length
;
return i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_WRITE
,command
,
I2C_SMBUS_BLOCK_DATA
,&data
);
}
extern s32 i2c_smbus_write_i2c_block_data
(struct i2c_client
* client
,
u8 command
, u8 length
, u8
*values
)
{
union i2c_smbus_data data
;
int i
;
if (length
> 32)
length
= 32;
for (i
= 1; i
<= length
; i
++)
data.
block[i
] = values
[i
-1];
data.
block[0] = length
;
return i2c_smbus_xfer
(client
->adapter
,client
->addr
,client
->flags
,
I2C_SMBUS_WRITE
,command
,
I2C_SMBUS_I2C_BLOCK_DATA
,&data
);
}
/* Simulate a SMBus command using the i2c protocol
No checking of parameters is done! */
static s32 i2c_smbus_xfer_emulated
(struct i2c_adapter
* adapter
, u16 addr
,
unsigned short flags
,
char read_write
, u8 command
, int size
,
union i2c_smbus_data
* data
)
{
/* So we need to generate a series of msgs. In the case of writing, we
need to use only one message; when reading, we need two. We initialize
most things with sane defaults, to keep the code below somewhat
simpler. */
unsigned char msgbuf0
[34];
unsigned char msgbuf1
[34];
int num
= read_write
== I2C_SMBUS_READ
?2:1;
struct i2c_msg msg
[2] = { { addr
, flags
, 1, msgbuf0
},
{ addr
, flags
| I2C_M_RD
, 0, msgbuf1
}
};
int i
;
msgbuf0
[0] = command
;
switch(size
) {
case I2C_SMBUS_QUICK
:
msg
[0].
len = 0;
/* Special case: The read/write field is used as data */
msg
[0].
flags = flags
| (read_write
==I2C_SMBUS_READ
)?I2C_M_RD
:0;
num
= 1;
break;
case I2C_SMBUS_BYTE
:
if (read_write
== I2C_SMBUS_READ
) {
/* Special case: only a read! */
msg
[0].
flags = I2C_M_RD
| flags
;
num
= 1;
}
break;
case I2C_SMBUS_BYTE_DATA
:
if (read_write
== I2C_SMBUS_READ
)
msg
[1].
len = 1;
else {
msg
[0].
len = 2;
msgbuf0
[1] = data
->byte
;
}
break;
case I2C_SMBUS_WORD_DATA
:
if (read_write
== I2C_SMBUS_READ
)
msg
[1].
len = 2;
else {
msg
[0].
len=3;
msgbuf0
[1] = data
->word
& 0xff;
msgbuf0
[2] = (data
->word
>> 8) & 0xff;
}
break;
case I2C_SMBUS_PROC_CALL
:
num
= 2; /* Special case */
msg
[0].
len = 3;
msg
[1].
len = 2;
msgbuf0
[1] = data
->word
& 0xff;
msgbuf0
[2] = (data
->word
>> 8) & 0xff;
break;
case I2C_SMBUS_BLOCK_DATA
:
if (read_write
== I2C_SMBUS_READ
) {
cprintf
("i2c-core.o: Block read not supported under "
"I2C emulation!\n");
return -1;
} else {
msg
[0].
len = data
->block
[0] + 2;
if (msg
[0].
len > 34) {
cprintf
("i2c-core.o: smbus_access called with "
"invalid block write size (%d)\n",
msg
[0].
len);
return -1;
}
for (i
= 1; i
<= msg
[0].
len; i
++)
msgbuf0
[i
] = data
->block
[i
-1];
}
break;
default:
cprintf
("i2c-core.o: smbus_access called with invalid size (%d)\n",
size
);
return -1;
}
if (i2c_transfer
(adapter
, msg
, num
) < 0)
return -1;
if (read_write
== I2C_SMBUS_READ
)
switch(size
) {
case I2C_SMBUS_BYTE
:
data
->byte
= msgbuf0
[0];
break;
case I2C_SMBUS_BYTE_DATA
:
data
->byte
= msgbuf1
[0];
break;
case I2C_SMBUS_WORD_DATA
:
case I2C_SMBUS_PROC_CALL
:
data
->word
= msgbuf1
[0] | (msgbuf1
[1] << 8);
break;
}
return 0;
}
s32 i2c_smbus_xfer
(struct i2c_adapter
* adapter
, u16 addr
, unsigned short flags
,
char read_write
, u8 command
, int size
,
union i2c_smbus_data
* data
)
{
s32 res
;
flags
= flags
& I2C_M_TEN
;
if (adapter
->algo
->smbus_xfer
) {
res
= adapter
->algo
->smbus_xfer
(adapter
,addr
,flags
,read_write
,
command
,size
,data
);
} else
res
= i2c_smbus_xfer_emulated
(adapter
,addr
,flags
,read_write
,
command
,size
,data
);
return res
;
}
/* You should always define `functionality'; the 'else' is just for
backward compatibility. */
u32 i2c_get_functionality
(struct i2c_adapter
*adap
)
{
if (adap
->algo
->functionality
)
return adap
->algo
->functionality
(adap
);
else
return 0xffffffff;
}
int i2c_check_functionality
(struct i2c_adapter
*adap
, u32 func
)
{
u32 adap_func
= i2c_get_functionality
(adap
);
return (func
& adap_func
) == func
;
}
static int i2c_init
(void)
{
printk
(KERN_INFO
"i2c-core.o: i2c core module\n");
memset(adapters
,0,sizeof(adapters
));
memset(drivers
,0,sizeof(drivers
));
adap_count
=0;
driver_count
=0;
return 0;
}
extern void i2c_algo_bit_init
(void);
int i2c_init_all
(void)
{
/* --------------------- global ----- */
i2c_init
();
i2c_algo_bit_init
();
return 0;
}