From c8a797a98cb63afd620d3ae448e8ee3e45f47088 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 15:30:45 +0800 Subject: [PATCH] greybus: Import most recent greybus code to new repo. --- drivers/staging/greybus/Makefile | 22 ++++ drivers/staging/greybus/core.c | 153 +++++++++++++++++++++++++++ drivers/staging/greybus/greybus.h | 97 +++++++++++++++++ drivers/staging/greybus/greybus_id.h | 27 +++++ drivers/staging/greybus/i2c-gb.c | 122 +++++++++++++++++++++ 5 files changed, 421 insertions(+) create mode 100644 drivers/staging/greybus/Makefile create mode 100644 drivers/staging/greybus/core.c create mode 100644 drivers/staging/greybus/greybus.h create mode 100644 drivers/staging/greybus/greybus_id.h create mode 100644 drivers/staging/greybus/i2c-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile new file mode 100644 index 000000000000..432ad4c72863 --- /dev/null +++ b/drivers/staging/greybus/Makefile @@ -0,0 +1,22 @@ +greybus-y := core.o + +obj-m += greybus.o +obj-m += i2c-gb.o + +KERNELVER ?= $(shell uname -r) +KERNELDIR ?= /lib/modules/$(KERNELVER)/build +PWD := $(shell pwd) + +all: module + +module: + $(MAKE) -C $(KERNELDIR) M=$(PWD) + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers + +coccicheck: + $(MAKE) -C $(KERNELDIR) M=$(PWD) coccicheck + diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c new file mode 100644 index 000000000000..87148cd38b56 --- /dev/null +++ b/drivers/staging/greybus/core.c @@ -0,0 +1,153 @@ +/* + * Greybus "Core" + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "greybus.h" + +/* Allow greybus to be disabled at boot if needed */ +static bool nogreybus; +#ifdef MODULE +module_param(nogreybus, bool, 0444); +#else +core_param(nogreybus, bool, 0444); +#endif +int greybus_disabled(void) +{ + return nogreybus; +} +EXPORT_SYMBOL_GPL(greybus_disabled); + +static int greybus_match_one_id(struct greybus_device *gdev, + const struct greybus_device_id *id) +{ + struct greybus_descriptor *des = &gdev->descriptor; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && + (des->wVendor != id->wVendor)) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && + (des->wProduct != id->wProduct)) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && + (des->lSerialNumber != id->lSerialNumber)) + return 0; + + return 1; +} + +static const struct greybus_device_id *greybus_match_id( + struct greybus_device *gdev, + const struct greybus_device_id *id) +{ + if (id == NULL) + return NULL; + + for (; id->wVendor || id->wProduct || id->lSerialNumber || + id->driver_info ; id++) { + if (greybus_match_one_id(gdev, id)) + return id; + } + + return NULL; +} + +static int greybus_device_match(struct device *dev, struct device_driver *drv) +{ + struct greybus_driver *driver = to_greybus_driver(dev->driver); + struct greybus_device *gdev = to_greybus_device(dev); + const struct greybus_device_id *id; + + id = greybus_match_id(gdev, driver->id_table); + if (id) + return 1; + /* FIXME - Dyanmic ids? */ + return 0; +} + +static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* struct greybus_device *gdev = to_greybus_device(dev); */ + + /* FIXME - add some uevents here... */ + return 0; +} + +struct bus_type greybus_bus_type = { + .name = "greybus", + .match = greybus_device_match, + .uevent = greybus_uevent, +}; + +static int greybus_probe(struct device *dev) +{ + struct greybus_driver *driver = to_greybus_driver(dev->driver); + struct greybus_device *gdev = to_greybus_device(dev); + const struct greybus_device_id *id; + int retval; + + /* match id */ + id = greybus_match_id(gdev, driver->id_table); + if (!id) + return -ENODEV; + + retval = driver->probe(gdev, id); + if (retval) + return retval; + + return 0; +} + +static int greybus_remove(struct device *dev) +{ + struct greybus_driver *driver = to_greybus_driver(dev->driver); + struct greybus_device *gdev = to_greybus_device(dev); + + driver->disconnect(gdev); + return 0; +} + +int greybus_register_driver(struct greybus_driver *driver, struct module *owner, + const char *mod_name) +{ + int retval; + + if (greybus_disabled()) + return -ENODEV; + + driver->driver.name = driver->name; + driver->driver.probe = greybus_probe; + driver->driver.remove = greybus_remove; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + + retval = driver_register(&driver->driver); + if (retval) + return retval; + + pr_info("registered new driver %s\n", driver->name); + return 0; +} +EXPORT_SYMBOL_GPL(greybus_register_driver); + +void greybus_deregister(struct greybus_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(greybus_deregister); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h new file mode 100644 index 000000000000..8158e45393a9 --- /dev/null +++ b/drivers/staging/greybus/greybus.h @@ -0,0 +1,97 @@ +/* + * Greybus driver and device API + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __LINUX_GREYBUS_H +#define __LINUX_GREYBUS_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include "greybus_id.h" + + +#define GREYBUS_DEVICE_ID_MATCH_DEVICE \ + (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) + +#define GREYBUS_DEVICE(vendor, product) \ + .match_flags = GREYBUS_DEVICE_ID_MATCH_DEVICE, \ + .wVendor = (vendor), \ + .wProduct = (product), + +#define GREYBUS_DEVICE_SERIAL(serial) \ + .match_flags = GREYBUS_DEVICE_ID_MATCH_SERIAL, \ + .lSerial = (serial), + + +struct greybus_descriptor { + __u16 wVendor; + __u16 wProduct; + __u64 lSerialNumber; +}; + +struct greybus_device { + struct device dev; + struct greybus_descriptor descriptor; +}; +#define to_greybus_device(d) container_of(d, struct greybus_device, dev) + +struct greybus_driver { + const char *name; + + int (*probe) (struct greybus_device *gdev, + const struct greybus_device_id *id); + void (*disconnect) (struct greybus_device *gdev); + + int (*suspend) (struct greybus_device *gdev, pm_message_t message); + int (*resume) (struct greybus_device *gdev); + + const struct greybus_device_id *id_table; + + struct device_driver driver; +}; +#define to_greybus_driver(d) container_of(d, struct greybus_driver, driver) + +static inline void greybus_set_drvdata(struct greybus_device *gdev, void *data) +{ + dev_set_drvdata(&gdev->dev, data); +} + +static inline void *greybus_get_drvdata(struct greybus_device *gdev) +{ + return dev_get_drvdata(&gdev->dev); +} + +/* Don't call these directly, use the module_greybus_driver() macro instead */ +int greybus_register_driver(struct greybus_driver *driver, + struct module *module, const char *mod_name); +void greybus_deregister(struct greybus_driver *driver); + +/* define to get proper THIS_MODULE and KBUILD_MODNAME values */ +#define greybus_register(driver) \ + greybus_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) + +/** + * module_greybus_driver() - Helper macro for registering a Greybus driver + * @__greybus_driver: greybus_driver structure + * + * Helper macro for Greybus drivers to set up proper module init / exit + * functions. Replaces module_init() and module_exit() and keeps people from + * printing pointless things to the kernel log when their driver is loaded. + */ +#define module_greybus_driver(__greybus_driver) \ + module_driver(__greybus_driver, greybus_register, greybus_deregister) + +extern struct bus_type greybus_bus_type; + +int greybus_disabled(void); + + +#endif /* __KERNEL__ */ +#endif /* __LINUX_GREYBUS_H */ diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h new file mode 100644 index 000000000000..4afbfe2d5cb9 --- /dev/null +++ b/drivers/staging/greybus/greybus_id.h @@ -0,0 +1,27 @@ +/* FIXME + * move this to include/linux/mod_devicetable.h when merging + */ + +#ifndef __LINUX_GREYBUS_ID_H +#define __LINUX_GREYBUS_ID_H + +#include +#include + + +struct greybus_device_id { + __u16 match_flags; + __u16 wVendor; + __u16 wProduct; + __u64 lSerialNumber; + + kernel_ulong_t driver_info + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; + +/* Used to match the greybus_device_id */ +#define GREYBUS_DEVICE_ID_MATCH_VENDOR BIT(0) +#define GREYBUS_DEVICE_ID_MATCH_PRODUCT BIT(1) +#define GREYBUS_DEVICE_ID_MATCH_SERIAL BIT(2) + +#endif /* __LINUX_GREYBUS_H */ diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c new file mode 100644 index 000000000000..a481e06c8b67 --- /dev/null +++ b/drivers/staging/greybus/i2c-gb.c @@ -0,0 +1,122 @@ +/* + * I2C bridge driver for the Greybus "generic" I2C module. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct i2c_gb_data { + struct i2c_adapter *adapter; + struct greybus_device *gdev; +}; + +static const struct greybus_device_id id_table[] = { + { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */ + { }, /* terminating NULL entry */ +}; + +/* We BETTER be able to do SMBUS protocl calls, otherwise we are bit-banging the + * slowest thing possible over the fastest bus possible, crazy... + * FIXME - research this, for now just assume we can + */ + + +static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_gb_data *i2c_gb_data; + struct greybus_device *gdev; + + i2c_gb_data = i2c_get_adapdata(adap); + gdev = i2c_gb_data->gdev; + + // FIXME - do the actual work of sending a i2c message here... + switch (size) { + case I2C_SMBUS_QUICK: + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_BROKEN: + case I2C_SMBUS_BLOCK_PROC_CALL: + case I2C_SMBUS_I2C_BLOCK_DATA: + default: + dev_err(&gdev->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + return 0; +} + +static u32 i2c_gb_func(struct i2c_adapter *adapter) +{ + // FIXME - someone figure out what we really can support, for now just guess... + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | + I2C_FUNC_SMBUS_PEC | + I2C_FUNC_SMBUS_READ_I2C_BLOCK; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = i2c_gb_access, + .functionality = i2c_gb_func, +}; + +static int i2c_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +{ + struct i2c_gb_data *i2c_gb_data; + struct i2c_adapter *adapter; + + i2c_gb_data = kzalloc(sizeof(*i2c_gb_data), GFP_KERNEL); + if (!i2c_gb_data) + return -ENOMEM; + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); + if (!adapter) { + kfree(i2c_gb_data); + return -ENOMEM; + } + + i2c_set_adapdata(adapter, i2c_gb_data); + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adapter->algo = &smbus_algorithm; + + i2c_gb_data->gdev = gdev; + i2c_gb_data->adapter = adapter; + + greybus_set_drvdata(gdev, i2c_gb_data); + return 0; +} + +static void i2c_gb_disconnect(struct greybus_device *gdev) +{ + struct i2c_gb_data *i2c_gb_data; + + i2c_gb_data = greybus_get_drvdata(gdev); + i2c_del_adapter(i2c_gb_data->adapter); + kfree(i2c_gb_data->adapter); + kfree(i2c_gb_data); +} + +static struct greybus_driver i2c_gb_driver = { + .probe = i2c_gb_probe, + .disconnect = i2c_gb_disconnect, + .id_table = id_table, +}; + +module_greybus_driver(i2c_gb_driver); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman ");