杨创开发板提供,仿照I2C的系统模型写的。
adapter的代码
/* * L3 bus algorithm module. * * Copyright (C) 2001 Russell King, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Note that L3 buses can share the same pins as I2C buses, so we must * _not_ generate an I2C start condition. An I2C start condition is * defined as a high-to-low transition of the data line while the clock * is high. Therefore, we must only change the data line while the * clock is low. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/l3/l3.h> #include <linux/l3/algo-bit.h> #define setdat(adap,val) adap->setdat(adap->data, val) #define setclk(adap,val) adap->setclk(adap->data, val) #define setmode(adap,val) adap->setmode(adap->data, val) #define setdatin(adap) adap->setdir(adap->data, 1) #define setdatout(adap) adap->setdir(adap->data, 0) #define getdat(adap) adap->getdat(adap->data) /* * Send one byte of data to the chip. Data is latched into the chip on * the rising edge of the clock. */ static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte) { int i; for (i = 0; i < 8; i++) { setclk(adap, 0); udelay(adap->data_hold); setdat(adap, byte & 1); udelay(adap->data_setup); setclk(adap, 1); udelay(adap->clock_high); byte >>= 1; } } /* * Send a set of bytes to the chip. We need to pulse the MODE line * between each byte, but never at the start nor at the end of the * transfer. */ static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len) { int i; for (i = 0; i < len; i++) { if (i) { udelay(adap->mode_hold); setmode(adap, 0); udelay(adap->mode); } setmode(adap, 1); udelay(adap->mode_setup); sendbyte(adap, buf[i]); } } /* * Read one byte of data from the chip. Data is latched into the chip on * the rising edge of the clock. */ static unsigned int readbyte(struct l3_algo_bit_data *adap) { unsigned int byte = 0; int i; for (i = 0; i < 8; i++) { setclk(adap, 0); udelay(adap->data_hold + adap->data_setup); setclk(adap, 1); if (getdat(adap)) byte |= 1 << i; udelay(adap->clock_high); } return byte; } /* * Read a set of bytes from the chip. We need to pulse the MODE line * between each byte, but never at the start nor at the end of the * transfer. */ static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len) { int i; for (i = 0; i < len; i++) { if (i) { udelay(adap->mode_hold); setmode(adap, 0); } setmode(adap, 1); udelay(adap->mode_setup); buf[i] = readbyte(adap); } } static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num) { struct l3_algo_bit_data *adap = l3_adap->algo_data; int i; /* * If we share an I2C bus, ensure that it is in STOP mode */ setclk(adap, 1); setdat(adap, 1); setmode(adap, 1); setdatout(adap); udelay(adap->mode); for (i = 0; i < num; i++) { struct l3_msg *pmsg = &msgs[i]; if (!(pmsg->flags & L3_M_NOADDR)) { setmode(adap, 0); udelay(adap->mode_setup); sendbyte(adap, pmsg->addr); udelay(adap->mode_hold); } if (pmsg->flags & L3_M_RD) { setdatin(adap); readbytes(adap, pmsg->buf, pmsg->len); } else { setdatout(adap); sendbytes(adap, pmsg->buf, pmsg->len); } } /* * Ensure that we leave the bus in I2C stop mode. */ setclk(adap, 1); setdat(adap, 1); setmode(adap, 0); setdatin(adap); return num; } static struct l3_algorithm l3_bit_algo = { name: "L3 bit-shift algorithm", xfer: l3_xfer, }; int l3_bit_add_bus(struct l3_adapter *adap) { adap->algo = &l3_bit_algo; return l3_add_adapter(adap); } int l3_bit_del_bus(struct l3_adapter *adap) { return l3_del_adapter(adap); } EXPORT_SYMBOL(l3_bit_add_bus); EXPORT_SYMBOL(l3_bit_del_bus);
/* * drivers/l3/l3-bit-elfin.c * * $Id: l3-bit-elfin.c,v 1.2 2004/05/12 06:28:52 laputa Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 2004-04-28 : Kwanghyun la <[email protected]> * - modified for sharing module device driver of samsung arch * */ #include <linux/autoconf.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/l3/algo-bit.h> #include <asm/semaphore.h> #include <asm/system.h> #include <asm/hardware.h> #include <asm/mach-types.h> #include <asm/arch/regs-gpio.h> //#define GDEBUG #ifdef GDEBUG # define dprintk(x...) printk(x) #else # define dprintk(x...) #endif #define NAME "l3-bit-24x0-gpio" struct bit_data { unsigned int sda; unsigned int scl; unsigned int l3_mode; }; static int getsda(void *data) { struct bit_data *bits = data; return (int)s3c2410_gpio_getpin(bits->sda); } static DECLARE_MUTEX(l3_lock); #define LOCK &l3_lock /* * iPAQs need the clock line driven hard high and low. */ static void l3_setscl(void *data, int state) { struct bit_data *bits = data; unsigned long flags; local_irq_save(flags); if (state) { s3c2410_gpio_setpin(bits->scl, 1); } else { s3c2410_gpio_setpin(bits->scl, 0); } s3c2410_gpio_cfgpin( (bits->scl), S3C2410_GPB4_OUTP); s3c2410_gpio_pullup( (bits->scl), 1); local_irq_restore(flags); } static void l3_setsda(void *data, int state) { struct bit_data *bits = data; if (state) { s3c2410_gpio_setpin(bits->sda, 1); } else { s3c2410_gpio_setpin(bits->sda, 0); } } static void l3_setdir(void *data, int in) { struct bit_data *bits = data; unsigned long flags; local_irq_save(flags); if (in) { s3c2410_gpio_cfgpin( (bits->sda), S3C2410_GPB3_INP); s3c2410_gpio_pullup( (bits->sda), 1); } else { s3c2410_gpio_cfgpin( (bits->sda), S3C2410_GPB3_OUTP); s3c2410_gpio_pullup( (bits->sda), 1); } local_irq_restore(flags); } static void l3_setmode(void *data, int state) { struct bit_data *bits = data; if (state) { s3c2410_gpio_setpin(bits->l3_mode, 1); } else { s3c2410_gpio_setpin(bits->l3_mode, 0); } } static struct l3_algo_bit_data l3_bit_data = { data: NULL, setdat: l3_setsda, setclk: l3_setscl, setmode: l3_setmode, setdir: l3_setdir, getdat: getsda, data_hold: 1, data_setup: 1, clock_high: 1, mode_hold: 1, mode_setup: 1, }; static struct l3_adapter l3_adapter = { owner: THIS_MODULE, name: NAME, algo_data: &l3_bit_data, lock: LOCK, }; static int inline l3_start(struct bit_data *bits) { l3_bit_data.data = bits; return l3_bit_add_bus(&l3_adapter); } static void inline l3_end(void) { l3_bit_del_bus(&l3_adapter); } static struct bit_data bit_data; static int __init bus_init(void) { struct bit_data *bit = &bit_data; unsigned long flags; int ret; #if defined( CONFIG_ARCH_UTU2440) || defined (CONFIG_BOARD_S3C2440_SMDK) dprintk("l3_2440 0\n"); bit->sda = S3C2410_GPB3; bit->scl = S3C2410_GPB4; bit->l3_mode = S3C2410_GPB2; #endif if (!bit->sda) return -ENODEV; /* * Default level for L3 mode is low. */ local_irq_save(flags); /* L3 gpio interface set */ dprintk("l3_2440 1\n"); s3c2410_gpio_setpin(bit->l3_mode, 1); s3c2410_gpio_setpin(bit->scl, 1); s3c2410_gpio_cfgpin( (bit->scl), S3C2410_GPB4_OUTP); s3c2410_gpio_pullup( (bit->scl), 1); s3c2410_gpio_cfgpin( (bit->sda), S3C2410_GPB3_OUTP); s3c2410_gpio_pullup( (bit->sda), 1); s3c2410_gpio_cfgpin( (bit->l3_mode), S3C2410_GPB2_OUTP); s3c2410_gpio_pullup( (bit->l3_mode), 1); #if defined( CONFIG_ARCH_UTU2440) || defined (CONFIG_BOARD_S3C2440_SMDK) /* IIS gpio interface set */ dprintk("l3_2440 2\n"); /* GPE 0: I2SLRCK */ s3c2410_gpio_cfgpin( S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); s3c2410_gpio_pullup( S3C2410_GPE0, 0); /* GPE 1: I2SSCLK */ s3c2410_gpio_cfgpin( S3C2410_GPE1, S3C2410_GPE1_I2SSCLK); s3c2410_gpio_pullup( S3C2410_GPE1, 0); /* GPE 2: CDCLK */ s3c2410_gpio_cfgpin( S3C2410_GPE2, S3C2410_GPE2_CDCLK); s3c2410_gpio_pullup( S3C2410_GPE2, 0); /* GPE 3: I2SSDI */ s3c2410_gpio_cfgpin( S3C2410_GPE3, S3C2410_GPE3_I2SSDI); s3c2410_gpio_pullup( S3C2410_GPE3, 0); /* GPE 4: I2SSDO */ s3c2410_gpio_cfgpin( S3C2410_GPE4, S3C2410_GPE4_I2SSDO); s3c2410_gpio_pullup( S3C2410_GPE4, 0); #endif local_irq_restore(flags); ret = l3_start(bit); if (ret) l3_end(); printk("GPIO L3 bus interface for S3C2440, installed\n"); return ret; } static void __exit bus_exit(void) { l3_end(); printk("GPIO L3 bus interface for S3C2440, uninstalled\n"); } module_init(bus_init); module_exit(bus_exit);
/* * linux/drivers/l3/l3-core.c * * Copyright (C) 2001 Russell King * * General structure taken from i2c-core.c by 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. * * See linux/Documentation/l3 for further documentation. */ #include <linux/module.h> #include <linux/autoconf.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/proc_fs.h> #include <linux/kmod.h> #include <linux/init.h> #include <linux/l3/l3.h> static DECLARE_MUTEX(adapter_lock); static LIST_HEAD(adapter_list); static DECLARE_MUTEX(driver_lock); static LIST_HEAD(driver_list); /** * l3_add_adapter - register a new L3 bus adapter * @adap: l3_adapter structure for the registering adapter * * Make the adapter available for use by clients using name adap->name. * The adap->adapters list is initialised by this function. * * Returns 0; */ int l3_add_adapter(struct l3_adapter *adap) { INIT_LIST_HEAD(&adap->clients); down(&adapter_lock); list_add(&adap->adapters, &adapter_list); up(&adapter_lock); return 0; } /** * l3_del_adapter - unregister a L3 bus adapter * @adap: l3_adapter structure to unregister * * Remove an adapter from the list of available L3 Bus adapters. * * Returns 0; */ int l3_del_adapter(struct l3_adapter *adap) { down(&adapter_lock); list_del(&adap->adapters); up(&adapter_lock); return 0; } static struct l3_adapter *__l3_get_adapter(const char *name) { struct list_head *l; list_for_each(l, &adapter_list) { struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters); if (strcmp(adap->name, name) == 0) return adap; } return NULL; } /** * l3_get_adapter - get a reference to an adapter * @name: driver name * * Obtain a l3_adapter structure for the specified adapter. If the adapter * is not currently load, then load it. The adapter will be locked in core * until all references are released via l3_put_adapter. */ struct l3_adapter *l3_get_adapter(const char *name) { struct l3_adapter *adap = NULL; int try; for (try = 0; try < 2; try ++) { down(&adapter_lock); adap = __l3_get_adapter(name); if (adap && !try_module_get(adap->owner)) adap = NULL; up(&adapter_lock); if (adap) break; if (try == 0) request_module(name); } return adap; } /** * l3_put_adapter - release a reference to an adapter * @adap: driver to release reference * * Indicate to the L3 core that you no longer require the adapter reference. * The adapter module may be unloaded when there are no references to its * data structure. * * You must not use the reference after calling this function. */ void l3_put_adapter(struct l3_adapter *adap) { if (adap && adap->owner) module_put(adap->owner); } /** * l3_add_driver - register a new L3 device driver * @driver - driver structure to make available * * Make the driver available for use by clients using name driver->name. * The driver->drivers list is initialised by this function. * * Returns 0; */ int l3_add_driver(struct l3_driver *driver) { down(&driver_lock); list_add(&driver->drivers, &driver_list); up(&driver_lock); return 0; } /** * l3_del_driver - unregister a L3 device driver * @driver: driver to remove * * Remove an driver from the list of available L3 Bus device drivers. * * Returns 0; */ int l3_del_driver(struct l3_driver *driver) { down(&driver_lock); list_del(&driver->drivers); up(&driver_lock); return 0; } static struct l3_driver *__l3_get_driver(const char *name) { struct list_head *l; list_for_each(l, &driver_list) { struct l3_driver *drv = list_entry(l, struct l3_driver, drivers); if (strcmp(drv->name, name) == 0) return drv; } return NULL; } /** * l3_get_driver - get a reference to a driver * @name: driver name * * Obtain a l3_driver structure for the specified driver. If the driver is * not currently load, then load it. The driver will be locked in core * until all references are released via l3_put_driver. */ struct l3_driver *l3_get_driver(const char *name) { struct l3_driver *drv = NULL; int try; for (try = 0; try < 2; try ++) { down(&adapter_lock); drv = __l3_get_driver(name); if (drv && !try_module_get(drv->owner)) drv = NULL; up(&adapter_lock); if (drv) break; if (try == 0) request_module(name); } return drv; } /** * l3_put_driver - release a reference to a driver * @drv: driver to release reference * * Indicate to the L3 core that you no longer require the driver reference. * The driver module may be unloaded when there are no references to its * data structure. * * You must not use the reference after calling this function. */ void l3_put_driver(struct l3_driver *drv) { if (drv && drv->owner) module_put(drv->owner); } /** * l3_attach_client - attach a client to an adapter and driver * @client: client structure to attach * @adap: adapter (module) name * @drv: driver (module) name * * Attempt to attach a client (a user of a device driver) to a particular * driver and adapter. If the specified driver or adapter aren't registered, * request_module is used to load the relevant modules. * * Returns 0 on success, or negative error code. */ int l3_attach_client(struct l3_client *client, const char *adap, const char *drv) { struct l3_adapter *adapter = l3_get_adapter(adap); struct l3_driver *driver = l3_get_driver(drv); int ret = -ENOENT; if (!adapter) printk(KERN_ERR "%s: unable to get adapter: %s\n",__FUNCTION__ , adap); if (!driver) printk(KERN_ERR "%s: unable to get driver: %s\n", __FUNCTION__ , drv); if (adapter && driver) { ret = 0; client->adapter = adapter; client->driver = driver; list_add(&client->__adap, &adapter->clients); if (driver->attach_client) ret = driver->attach_client(client); } if (ret) { l3_put_driver(driver); l3_put_adapter(adapter); } return ret; } /** * l3_detach_client - detach a client from an adapter and driver * @client: client structure to detach * * Detach the client from the adapter and driver. */ int l3_detach_client(struct l3_client *client) { struct l3_adapter *adapter = client->adapter; struct l3_driver *driver = client->driver; driver->detach_client(client); client->adapter = NULL; client->driver = NULL; l3_put_driver(driver); l3_put_adapter(adapter); list_del(&client->__adap); return 0; } /** * l3_transfer - transfer information on an L3 bus * @adap: adapter structure to perform transfer on * @msgs: array of l3_msg structures describing transfer * @num: number of l3_msg structures * * Transfer the specified messages to/from a device on the L3 bus. * * Returns number of messages successfully transferred, otherwise negative * error code. */ int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) { int ret = -ENOSYS; if (adap->algo->xfer) { down(adap->lock); ret = adap->algo->xfer(adap, msgs, num); up(adap->lock); } return ret; } /** * l3_write - send data to a device on an L3 bus * @client: registered client structure * @addr: L3 bus address * @buf: buffer for bytes to send * @len: number of bytes to send * * Send len bytes pointed to by buf to device address addr on the L3 bus * described by client. * * Returns the number of bytes transferred, or negative error code. */ int l3_write(struct l3_client *client, int addr, const char *buf, int len) { struct l3_adapter *adap = client->adapter; struct l3_msg msg; int ret; msg.addr = addr; msg.flags = 0; msg.buf = (char *)buf; msg.len = len; ret = l3_transfer(adap, &msg, 1); return ret == 1 ? len : ret; } /** * l3_read - receive data from a device on an L3 bus * @client: registered client structure * @addr: L3 bus address * @buf: buffer for bytes to receive * @len: number of bytes to receive * * Receive len bytes from device address addr on the L3 bus described by * client to a buffer pointed to by buf. * * Returns the number of bytes transferred, or negative error code. */ int l3_read(struct l3_client *client, int addr, char *buf, int len) { struct l3_adapter *adap = client->adapter; struct l3_msg msg; int ret; msg.addr = addr; msg.flags = L3_M_RD; msg.buf = buf; msg.len = len; ret = l3_transfer(adap, &msg, 1); return ret == 1 ? len : ret; } EXPORT_SYMBOL(l3_add_adapter); EXPORT_SYMBOL(l3_del_adapter); EXPORT_SYMBOL(l3_get_adapter); EXPORT_SYMBOL(l3_put_adapter); EXPORT_SYMBOL(l3_add_driver); EXPORT_SYMBOL(l3_del_driver); EXPORT_SYMBOL(l3_get_driver); EXPORT_SYMBOL(l3_put_driver); EXPORT_SYMBOL(l3_attach_client); EXPORT_SYMBOL(l3_detach_client); EXPORT_SYMBOL(l3_transfer); EXPORT_SYMBOL(l3_write); EXPORT_SYMBOL(l3_read);
/* * linux/include/linux/l3/l3.h * * Copyright (C) 2001 Russell King, All Rights Reserved. * * 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. * * Derived from i2c.h by Simon G. Vogl */ #ifndef L3_H #define L3_H struct l3_msg { unsigned char addr; /* slave address */ unsigned char flags; #define L3_M_RD 0x01 #define L3_M_NOADDR 0x02 unsigned short len; /* msg length */ unsigned char *buf; /* pointer to msg data */ }; #ifdef __KERNEL__ #include <linux/types.h> #include <linux/list.h> struct l3_client; struct l3_ops { int (*open)(struct l3_client *); int (*command)(struct l3_client *, int cmd, void *arg); void (*close)(struct l3_client *); }; /* * A driver is capable of handling one or more physical devices present on * L3 adapters. This information is used to inform the driver of adapter * events. */ struct l3_driver { /* * This name is used to uniquely identify the driver. * It should be the same as the module name. */ char name[32]; /* * Notifies the driver that a new client wishes to use its * services. Note that the module use count will be increased * prior to this function being called. In addition, the * clients driver and adapter fields will have been setup. */ int (*attach_client)(struct l3_client *); /* * Notifies the driver that the client has finished with its * services, and any memory that it allocated for this client * should be cleaned up. In addition the chip should be * shut down. */ void (*detach_client)(struct l3_client *); /* * Possible operations on the driver. */ struct l3_ops *ops; /* * Module structure, if any. */ struct module *owner; /* * drivers list */ struct list_head drivers; }; struct l3_adapter; struct l3_algorithm { /* textual description */ char name[32]; /* perform bus transactions */ int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num); }; struct semaphore; /* * l3_adapter is the structure used to identify a physical L3 bus along * with the access algorithms necessary to access it. */ struct l3_adapter { /* * This name is used to uniquely identify the adapter. * It should be the same as the module name. */ char name[32]; /* * the algorithm to access the bus */ struct l3_algorithm *algo; /* * Algorithm specific data */ void *algo_data; /* * This may be NULL, or should point to the module struct */ struct module *owner; /* * private data for the adapter */ void *data; /* * Our lock. Unlike the i2c layer, we allow this to be used for * other stuff, like the i2c layer lock. Some people implement * i2c stuff using the same signals as the l3 bus. */ struct semaphore *lock; /* * List of attached clients. */ struct list_head clients; /* * List of all adapters. */ struct list_head adapters; }; /* * l3_client identifies a single device (i.e. chip) that is connected to an * L3 bus. The behaviour is defined by the routines of the driver. This * function is mainly used for lookup & other admin. functions. */ struct l3_client { struct l3_adapter *adapter; /* the adapter we sit on */ struct l3_driver *driver; /* and our access routines */ void *driver_data; /* private driver data */ struct list_head __adap; }; extern int l3_add_adapter(struct l3_adapter *); extern int l3_del_adapter(struct l3_adapter *); extern int l3_add_driver(struct l3_driver *); extern int l3_del_driver(struct l3_driver *); #if 0 extern void l3_put_adapter(struct l3_adapter *); extern struct l3_adapter *l3_get_adapter(const char *name); extern int l3_write(struct l3_adapter *, int, const char *, int); extern int l3_read(struct l3_adapter *, int, char *, int); #else // ghcstop add extern int l3_attach_client(struct l3_client *, const char *, const char *); extern int l3_detach_client(struct l3_client *); extern int l3_transfer(struct l3_adapter *, struct l3_msg msgs[], int); extern int l3_write(struct l3_client *, int, const char *, int); extern int l3_read(struct l3_client *, int, char *, int); /** * l3_command - send a command to a L3 device driver * @client: registered client structure * @cmd: device driver command * @arg: device driver arguments * * Ask the L3 device driver to perform some function. Further information * should be sought from the device driver in question. * * Returns negative error code on failure. */ static inline int l3_command(struct l3_client *clnt, int cmd, void *arg) { struct l3_ops *ops = clnt->driver->ops; int ret = -EINVAL; if (ops && ops->command) ret = ops->command(clnt, cmd, arg); return ret; } static inline int l3_open(struct l3_client *clnt) { struct l3_ops *ops = clnt->driver->ops; int ret = 0; if (ops && ops->open) ret = ops->open(clnt); return ret; } static inline void l3_close(struct l3_client *clnt) { struct l3_ops *ops = clnt->driver->ops; if (ops && ops->close) ops->close(clnt); } #endif #endif #endif /* L3_H */