/*
 * (C) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#ifndef DRIVER_H
#define DRIVER_H

#include <linux/list.h>
#include <linux/ioport.h>

#define MAX_DRIVER_NAME		32
#define FORMAT_DRIVER_NAME_ID	"%s%d"

#include <param.h>

/**
 * @file
 * @brief Main description of the device/driver model
 */

/** @page driver_model Main description of the device/driver model
 *
 * We follow a rather simplistic driver model here. There is a
 * @code struct device_d @endcode
 * which describes a particular device present in the system.
 *
 * On the other side a
 * @code struct driver_d @endcode
 * represents a driver present in the system.
 *
 * Both structs find together via the members 'type' (int) and 'name' (char *).
 * If both members match, the driver's probe function is called with the
 * struct device_d as argument.
 *
 * People familiar with the Linux platform bus will recognize this behaviour
 * and in fact many things were stolen from there. Some selected members of the
 * structs will be described in this document.
 */

/*@{*/	/* do not delete, doxygen relevant */

struct filep;
struct bus_type;

/** @brief Describes a particular device present in the system */
struct device_d {
	/*! This member (and 'type' described below) is used to match with a
	 * driver. This is a descriptive name and could be MPC5XXX_ether or
	 * imx_serial. */
	char name[MAX_DRIVER_NAME];
	/*! The id is used to uniquely identify a device in the system. The id
	 * will show up under /dev/ as the device's name. Usually this is
	 * something like eth0 or nor0. */
	int id;

	struct resource *resource;
	int num_resources;

	void *platform_data; /*! board specific information about this device */

	/*! Devices of a particular class normaly need to store more
	 * information than struct device holds.
	 */
	void *priv;
	void *type_data;     /*! In case this device is a specific device, this pointer
			      * points to the type specific device, i.e. eth_device
			      */
	struct driver_d *driver; /*! The driver for this device */

	struct list_head list;     /* The list of all devices */
	struct list_head children; /* our children            */
	struct list_head sibling;
	struct list_head active;   /* The list of all devices which have a driver */

	struct device_d *parent;   /* our parent, NULL if not present */

	struct bus_type *bus;

	/*! The parameters for this device. This is used to carry information
	 * of board specific data from the board code to the device driver. */
	struct list_head parameters;

	struct list_head cdevs;
};

/** @brief Describes a driver present in the system */
struct driver_d {
	/*! The name of this driver. Used to match to
	 * the corresponding device. */
	char name[MAX_DRIVER_NAME];

	struct list_head list;

	/*! Called if an instance of a device is found */
	int     (*probe) (struct device_d *);

	/*! Called if an instance of a device is gone. */
	void     (*remove)(struct device_d *);

	void    (*info) (struct device_d *);
	void    (*shortinfo) (struct device_d *);

	unsigned long type;
	struct bus_type *bus;

	/*! This is somewhat redundant with the type data in struct device.
	 * Currently the filesystem implementation uses this field while
	 * ethernet drivers use the same field in struct device. Probably
	 * one of both should be removed. */
	void *type_data;
};

/*@}*/	/* do not delete, doxygen relevant */

#define RW_SIZE(x)      (x)
#define RW_SIZE_MASK    0x7

/* Register devices and drivers.
 */
int register_driver(struct driver_d *);
int register_device(struct device_d *);

/* Unregister a device. This function can fail, e.g. when the device
 * has children.
 */
int unregister_device(struct device_d *);

/* Organize devices in a tree. These functions do _not_ register or
 * unregister a device. Only registered devices are allowed here.
 */
int dev_add_child(struct device_d *dev, struct device_d *child);

/* Iterate over a devices children
 */
#define device_for_each_child(dev, child) \
	list_for_each_entry(child, &dev->children, sibling)

/* Iterate over a devices children - Safe against removal version
 */
#define device_for_each_child_safe(dev, tmpdev, child) \
	list_for_each_entry_safe(child, tmpdev, &dev->children, sibling)

/* Iterate through the devices of a given type. if last is NULL, the
 * first device of this type is returned. Put this pointer in as
 * 'last' to get the next device. This functions returns NULL if no
 * more devices are found.
 */
struct device_d *get_device_by_type(ulong type, struct device_d *last);
struct device_d *get_device_by_id(const char *id);
struct device_d *get_device_by_name(const char *name);

/* Find a free device id from the given template. This is archieved by
 * appending a number to the template. Dynamically created devices should
 * use this function rather than filling the id field themselves.
 */
int get_free_deviceid(const char *name_template);

char *deviceid_from_spec_str(const char *str, char **endp);

extern const char *dev_id(const struct device_d *dev);

static inline const char *dev_name(const struct device_d *dev)
{
	return dev_id(dev);
}

/*
 * get register base 'num' for a device
 */
void __iomem *dev_get_mem_region(struct device_d *dev, int num);

/*
 * exlusively request register base 'num' for a device
 */
static inline void __iomem *dev_request_mem_region(struct device_d *dev, int num)
{
	/* no resource tracking yet */
	return dev_get_mem_region(dev, num);
}

/*
 * register a generic device
 * with only one resource
 */
struct device_d *add_generic_device(const char* devname, int id, const char *resname,
		resource_size_t start, resource_size_t size, unsigned int flags,
		void *pdata);

/*
 * register a generic device
 * with multiple resources
 */
struct device_d *add_generic_device_res(const char* devname, int id,
		struct resource *res, int nb, void *pdata);

/*
 * register a memory device
 */
static inline struct device_d *add_mem_device(const char *name, resource_size_t start,
		resource_size_t size, unsigned int flags)
{
	return add_generic_device("mem", -1, name, start, size,
				  IORESOURCE_MEM | flags, NULL);
}

static inline struct device_d *add_cfi_flash_device(int id, resource_size_t start,
		resource_size_t size, unsigned int flags)
{
	return add_generic_device("cfi_flash", id, NULL, start, size,
				  IORESOURCE_MEM | flags, NULL);
}

struct NS16550_plat;
static inline struct device_d *add_ns16550_device(int id, resource_size_t start,
		resource_size_t size, int flags, struct NS16550_plat *pdata)
{
	return add_generic_device("serial_ns16550", id, NULL, start, size,
				  IORESOURCE_MEM | flags, pdata);
}

#ifdef CONFIG_DRIVER_NET_DM9000
struct device_d *add_dm9000_device(int id, resource_size_t base,
		resource_size_t data, int flags, void *pdata);
#else
static inline struct device_d *add_dm9000_device(int id, resource_size_t base,
		resource_size_t data, int flags, void *pdata)
{
	return NULL;
}
#endif

#ifdef CONFIG_USB_EHCI
struct device_d *add_usb_ehci_device(int id, resource_size_t hccr,
		resource_size_t hcor, void *pdata);
#else
static inline struct device_d *add_usb_ehci_device(int id, resource_size_t hccr,
		resource_size_t hcor, void *pdata)
{
	return NULL;
}
#endif

static inline struct device_d *add_generic_usb_ehci_device(int id,
		resource_size_t base, void *pdata)
{
	return add_usb_ehci_device(id, base + 0x100, base + 0x140, pdata);
}

/* linear list over all available devices
 */
extern struct list_head device_list;

/* linear list over all available drivers
 */
extern struct list_head driver_list;

/* Iterate over all devices
 */
#define for_each_device(dev) list_for_each_entry(dev, &device_list, list)

/* Iterate over all drivers
 */
#define for_each_driver(drv) list_for_each_entry(drv, &driver_list, list)

/* Find a driver with the given name. Currently the filesystem implementation
 * uses this to get the driver from the name the user specifies with the
 * mount command
 */
struct driver_d *get_driver_by_name(const char *name);

struct cdev;

int     dev_protect(struct device_d *dev, size_t count, unsigned long offset, int prot);

/* These are used by drivers which work with direct memory accesses */
ssize_t mem_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags);
ssize_t mem_write(struct cdev *cdev, const void *buf, size_t count, ulong offset, ulong flags);
int mem_memmap(struct cdev *cdev, void **map, int flags);

/* Use this if you have nothing to do in your drivers probe function */
int dummy_probe(struct device_d *);

/* Iterate over all activated devices (i.e. the ones with drivers and shut
 * them down.
 */
void devices_shutdown(void);

int generic_memmap_ro(struct cdev *dev, void **map, int flags);
int generic_memmap_rw(struct cdev *dev, void **map, int flags);

static inline off_t dev_lseek_default(struct cdev *cdev, off_t ofs)
{
	return ofs;
}

static inline int dev_open_default(struct device_d *dev, struct filep *f)
{
	return 0;
}

static inline int dev_close_default(struct device_d *dev, struct filep *f)
{
	return 0;
}

/* debugging and troubleshooting/diagnostic helpers. */

#define dev_printf(dev, format, arg...)	\
	printf("%s@%s: " format , (dev)->name , \
	       dev_name(dev) , ## arg)

#define dev_emerg(dev, format, arg...)		\
	dev_printf((dev) , format , ## arg)
#define dev_alert(dev, format, arg...)		\
	dev_printf((dev) , format , ## arg)
#define dev_crit(dev, format, arg...)		\
	dev_printf((dev) , format , ## arg)
#define dev_err(dev, format, arg...)		\
	dev_printf(dev , format , ## arg)
#define dev_warn(dev, format, arg...)		\
	dev_printf((dev) , format , ## arg)
#define dev_notice(dev, format, arg...)		\
	dev_printf((dev) , format , ## arg)
#define dev_info(dev, format, arg...)		\
	dev_printf((dev) , format , ## arg)

#if defined(DEBUG)
#define dev_dbg(dev, format, arg...)		\
	dev_printf((dev) , format , ## arg)
#else
#define dev_dbg(dev, format, arg...)		\
	({ if (0) dev_printf((dev), format, ##arg); 0; })
#endif

struct bus_type {
	char *name;
	int (*match)(struct device_d *dev, struct driver_d *drv);
	int (*probe)(struct device_d *dev);
	void (*remove)(struct device_d *dev);

	struct list_head list;
};

extern struct bus_type platform_bus;

struct file_operations {
	/*! Called in response of reading from this device. Required */
	ssize_t (*read)(struct cdev*, void* buf, size_t count, ulong offset, ulong flags);

	/*! Called in response of write to this device. Required */
	ssize_t (*write)(struct cdev*, const void* buf, size_t count, ulong offset, ulong flags);

	int (*ioctl)(struct cdev*, int, void *);
	off_t (*lseek)(struct cdev*, off_t);
	int (*open)(struct cdev*);
	int (*close)(struct cdev*);
	int (*flush)(struct cdev*);
	int (*erase)(struct cdev*, size_t count, unsigned long offset);
	int (*protect)(struct cdev*, size_t count, unsigned long offset, int prot);
	int (*memmap)(struct cdev*, void **map, int flags);
};

struct cdev {
	struct file_operations *ops;
	void *priv;
	struct device_d *dev;
	struct list_head list;
	struct list_head devices_list;
	char *name;
	unsigned long offset;
	size_t size;
	unsigned int flags;
	int open;
	struct mtd_info *mtd;
};

int devfs_create(struct cdev *);
int devfs_remove(struct cdev *);
int cdev_find_free_index(const char *);
struct cdev *cdev_by_name(const char *filename);
struct cdev *cdev_open(const char *name, unsigned long flags);
void cdev_close(struct cdev *cdev);
int cdev_flush(struct cdev *cdev);
ssize_t cdev_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags);
ssize_t cdev_write(struct cdev *cdev, const void *buf, size_t count, ulong offset, ulong flags);
int cdev_ioctl(struct cdev *cdev, int cmd, void *buf);
int cdev_erase(struct cdev *cdev, size_t count, unsigned long offset);

#define DEVFS_PARTITION_FIXED		(1 << 0)
#define DEVFS_PARTITION_READONLY	(1 << 1)
#define DEVFS_IS_PARTITION		(1 << 2)

int devfs_add_partition(const char *devname, unsigned long offset, size_t size,
		int flags, const char *name);
int devfs_del_partition(const char *name);

#endif /* DRIVER_H */

