Message ID | 1522250043-8065-3-git-send-email-tharvey@gateworks.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Wed, Mar 28, 2018 at 8:14 AM, Tim Harvey <tharvey@gateworks.com> wrote: > The Gateworks System Controller (GSC) is an I2C slave controller > implemented with an MSP430 micro-controller whose firmware embeds the > following features: > - I/O expander (16 GPIO's) using PCA955x protocol > - Real Time Clock using DS1672 protocol > - User EEPROM using AT24 protocol > - HWMON using custom protocol > - Interrupt controller with tamper detect, user pushbotton > - Watchdog controller capable of full board power-cycle > - Power Control capable of full board power-cycle > > see http://trac.gateworks.com/wiki/gsc for more details > <snip> > + > +/* > + * gsc_powerdown - API to use GSC to power down board for a specific time > + * > + * secs - number of seconds to remain powered off > + */ > +static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs) > +{ > + int ret; > + unsigned char regs[4]; > + > + dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n", > + secs); > + regs[0] = secs & 0xff; > + regs[1] = (secs >> 8) & 0xff; > + regs[2] = (secs >> 16) & 0xff; > + regs[3] = (secs >> 24) & 0xff; > + ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4); > + > + return ret; > +} Any feedback on the 'powerdown' sysfs attribute that hooks to this function? This allows the GSC to disable the board primary power supply for 2^32 seconds and is often used to 'reset' the board although it could also be used to put the board in a power down state longer. I'm wondering if there is a more appropriate API for this in the kernel that I don't know about. I would also like to register a restart handler using this but I believe that ARM restart handlers currently can not use I2C - is that correct? Regards, Tim -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Apr 03, 2018 at 08:48:27AM -0700, Tim Harvey wrote: > On Wed, Mar 28, 2018 at 8:14 AM, Tim Harvey <tharvey@gateworks.com> wrote: > > The Gateworks System Controller (GSC) is an I2C slave controller > > implemented with an MSP430 micro-controller whose firmware embeds the > > following features: > > - I/O expander (16 GPIO's) using PCA955x protocol > > - Real Time Clock using DS1672 protocol > > - User EEPROM using AT24 protocol > > - HWMON using custom protocol > > - Interrupt controller with tamper detect, user pushbotton > > - Watchdog controller capable of full board power-cycle > > - Power Control capable of full board power-cycle > > > > see http://trac.gateworks.com/wiki/gsc for more details > > > <snip> > > + > > +/* > > + * gsc_powerdown - API to use GSC to power down board for a specific time > > + * > > + * secs - number of seconds to remain powered off > > + */ > > +static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs) > > +{ > > + int ret; > > + unsigned char regs[4]; > > + > > + dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n", > > + secs); > > + regs[0] = secs & 0xff; > > + regs[1] = (secs >> 8) & 0xff; > > + regs[2] = (secs >> 16) & 0xff; > > + regs[3] = (secs >> 24) & 0xff; > > + ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4); > > + > > + return ret; > > +} > > Any feedback on the 'powerdown' sysfs attribute that hooks to this > function? This allows the GSC to disable the board primary power > supply for 2^32 seconds and is often used to 'reset' the board > although it could also be used to put the board in a power down state > longer. I'm wondering if there is a more appropriate API for this in > the kernel that I don't know about. Hi Tim RTC can cause wakeup when an alarm is set. It looks like the DS1672 does not have this. But you are emulating the DS1672 right? You could add a second emulated RTC which does support an alarm? DS3232? > I would also like to register a restart handler using this but I > believe that ARM restart handlers currently can not use I2C - is that > correct? There are plenty which use GPIOs, or UARTs. Not seen any which use i2c. What do you think does not work at this point? Andrew -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Apr 3, 2018 at 9:47 AM, Andrew Lunn <andrew@lunn.ch> wrote: > On Tue, Apr 03, 2018 at 08:48:27AM -0700, Tim Harvey wrote: >> On Wed, Mar 28, 2018 at 8:14 AM, Tim Harvey <tharvey@gateworks.com> wrote: >> > The Gateworks System Controller (GSC) is an I2C slave controller >> > implemented with an MSP430 micro-controller whose firmware embeds the >> > following features: >> > - I/O expander (16 GPIO's) using PCA955x protocol >> > - Real Time Clock using DS1672 protocol >> > - User EEPROM using AT24 protocol >> > - HWMON using custom protocol >> > - Interrupt controller with tamper detect, user pushbotton >> > - Watchdog controller capable of full board power-cycle >> > - Power Control capable of full board power-cycle >> > >> > see http://trac.gateworks.com/wiki/gsc for more details >> > >> <snip> >> > + >> > +/* >> > + * gsc_powerdown - API to use GSC to power down board for a specific time >> > + * >> > + * secs - number of seconds to remain powered off >> > + */ >> > +static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs) >> > +{ >> > + int ret; >> > + unsigned char regs[4]; >> > + >> > + dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n", >> > + secs); >> > + regs[0] = secs & 0xff; >> > + regs[1] = (secs >> 8) & 0xff; >> > + regs[2] = (secs >> 16) & 0xff; >> > + regs[3] = (secs >> 24) & 0xff; >> > + ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4); >> > + >> > + return ret; >> > +} >> >> Any feedback on the 'powerdown' sysfs attribute that hooks to this >> function? This allows the GSC to disable the board primary power >> supply for 2^32 seconds and is often used to 'reset' the board >> although it could also be used to put the board in a power down state >> longer. I'm wondering if there is a more appropriate API for this in >> the kernel that I don't know about. > > Hi Tim > > RTC can cause wakeup when an alarm is set. It looks like the DS1672 > does not have this. But you are emulating the DS1672 right? You could > add a second emulated RTC which does support an alarm? DS3232? Andrew, Thanks for the response! An RTC alarm may indeed be a good route for the overall sleep functionality I will look into that. What about the 'reset' functionality? Is there something in the power supply API for hooking in a GPIO based power switch (in my case it would be i2c) as I would think that would be common for ATX supplies? I didn't see anything in Documentation/power. This is what led me to the restart handler idea. Ultimately when someone issues a 'reboot' I would like it to use the GSC to power-cycle the board. > >> I would also like to register a restart handler using this but I >> believe that ARM restart handlers currently can not use I2C - is that >> correct? > > There are plenty which use GPIOs, or UARTs. Not seen any which use > i2c. What do you think does not work at this point? I'll have to dig around for the e-mail thread. I recall someone else trying to implement a restart handler for something hanging off i2c and the issue was that by the time the (ARM) restart handler got called interrupts were disabled making i2c unreliable. I have hooked the ARM restart handler to my GSC powerdown in some kernels and have had mixed results. When the handler gets called from a clean 'reboot' things seem fine but if its called from some error condition that halts the kernel it seems that i2c may not be reliable anymore. Regards, Tim -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> What about the 'reset' functionality? Is there something in the power > supply API for hooking in a GPIO based power switch (in my case it > would be i2c) as I would think that would be common for ATX supplies? > I didn't see anything in Documentation/power. > > This is what led me to the restart handler idea. Ultimately when > someone issues a 'reboot' I would like it to use the GSC to > power-cycle the board. Hi Tim I think you end up with the same problem. By the time you need to turn the power supply off, too much of the kernel is shut down to be able to use I2C. And if you are in the middle of an Oops, you have no idea of the current state. Another I2C transaction could be under way etc. All the current reset drivers are pretty much self contained, atomic and use KISS hardware like a GPIO. Maybe you best bet is to see if you can find any other I2C PMICs which the kernel supports. Andrew -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Apr 04, 2018 at 03:12:39PM +0200, Andrew Lunn wrote: > > What about the 'reset' functionality? Is there something in the power > > supply API for hooking in a GPIO based power switch (in my case it > > would be i2c) as I would think that would be common for ATX supplies? > > I didn't see anything in Documentation/power. > > This is what led me to the restart handler idea. Ultimately when > > someone issues a 'reboot' I would like it to use the GSC to > > power-cycle the board. > I think you end up with the same problem. By the time you need to turn > the power supply off, too much of the kernel is shut down to be able > to use I2C. And if you are in the middle of an Oops, you have no idea > of the current state. Another I2C transaction could be under way etc. > All the current reset drivers are pretty much self contained, atomic > and use KISS hardware like a GPIO. > Maybe you best bet is to see if you can find any other I2C PMICs which > the kernel supports. Most systems have a handshake for final power down via asserting signals rather than using register writes, the final power down sequence usually runs way after software. There's a few things that don't which just unceremoniously cut the power earlier on without completing the full power down sequence which for all practical purposes mostly works.
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1d20a80..013df63 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -341,6 +341,19 @@ config MFD_EXYNOS_LPASS Select this option to enable support for Samsung Exynos Low Power Audio Subsystem. +config MFD_GATEWORKS_GSC + tristate "Gateworks System Controller" + depends on (I2C && OF) + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Enable support for the Gateworks System Controller found + on Gateworks Single Board Computers. + + To compile this driver as a module, choose M here: the + module will be called gsc. + config MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d9474ad..da0868ff 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o +obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o diff --git a/drivers/mfd/gateworks-gsc.c b/drivers/mfd/gateworks-gsc.c new file mode 100644 index 0000000..e081613 --- /dev/null +++ b/drivers/mfd/gateworks-gsc.c @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Gateworks Corporation + * + * The Gateworks System Controller (GSC) is a family of a multi-function + * "Power Management and System Companion Device" chips originally designed for + * use in Gateworks Single Board Computers. The control interface is I2C, + * at 100kbps, with an interrupt. + * + */ +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/gsc.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* + * The GSC suffers from an errata where occasionally during + * ADC cycles the chip can NAK I2C transactions. To ensure we have reliable + * register access we place retries around register access. + */ +#define I2C_RETRIES 3 + +static int gsc_regmap_regwrite(void *context, unsigned int reg, + unsigned int val) +{ + struct i2c_client *client = context; + int retry, ret; + + for (retry = 0; retry < I2C_RETRIES; retry++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + /* + * -EAGAIN returned when the i2c host controller is busy + * -EIO returned when i2c device is busy + */ + if (ret != -EAGAIN && ret != -EIO) + break; + } + + return 0; +} + +static int gsc_regmap_regread(void *context, unsigned int reg, + unsigned int *val) +{ + struct i2c_client *client = context; + int retry, ret; + + for (retry = 0; retry < I2C_RETRIES; retry++) { + ret = i2c_smbus_read_byte_data(client, reg); + /* + * -EAGAIN returned when the i2c host controller is busy + * -EIO returned when i2c device is busy + */ + if (ret != -EAGAIN && ret != -EIO) + break; + } + *val = ret & 0xff; + + return 0; +} + +static struct regmap_bus regmap_gsc = { + .reg_write = gsc_regmap_regwrite, + .reg_read = gsc_regmap_regread, +}; + +/* + * gsc_powerdown - API to use GSC to power down board for a specific time + * + * secs - number of seconds to remain powered off + */ +static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs) +{ + int ret; + unsigned char regs[4]; + + dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n", + secs); + regs[0] = secs & 0xff; + regs[1] = (secs >> 8) & 0xff; + regs[2] = (secs >> 16) & 0xff; + regs[3] = (secs >> 24) & 0xff; + ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4); + + return ret; +} + +static ssize_t gsc_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gsc_dev *gsc = dev_get_drvdata(dev); + const char *name = attr->attr.name; + int rz = 0; + + if (strcasecmp(name, "fw_version") == 0) + rz = sprintf(buf, "%d\n", gsc->fwver); + else if (strcasecmp(name, "fw_crc") == 0) + rz = sprintf(buf, "0x%04x\n", gsc->fwcrc); + + return rz; +} + +static ssize_t gsc_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gsc_dev *gsc = dev_get_drvdata(dev); + const char *name = attr->attr.name; + int ret; + + if (strcasecmp(name, "powerdown") == 0) { + long value; + + ret = kstrtol(buf, 0, &value); + if (ret == 0) + gsc_powerdown(gsc, value); + } else + dev_err(dev, "invalid name '%s\n", name); + + return count; +} + +static struct device_attribute attr_fwver = + __ATTR(fw_version, 0440, gsc_show, NULL); +static struct device_attribute attr_fwcrc = + __ATTR(fw_crc, 0440, gsc_show, NULL); +static struct device_attribute attr_pwrdown = + __ATTR(powerdown, 0220, NULL, gsc_store); + +static struct attribute *gsc_attrs[] = { + &attr_fwver.attr, + &attr_fwcrc.attr, + &attr_pwrdown.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = gsc_attrs, +}; + +static const struct of_device_id gsc_of_match[] = { + { .compatible = "gw,gsc", }, + { } +}; + +static const struct regmap_config gsc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .max_register = 0xf, +}; + +static const struct regmap_config gsc_regmap_hwmon_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .max_register = 0x37, +}; + +static const struct regmap_irq gsc_irqs[] = { + REGMAP_IRQ_REG(GSC_IRQ_PB, 0, BIT(GSC_IRQ_PB)), + REGMAP_IRQ_REG(GSC_IRQ_KEY_ERASED, 0, BIT(GSC_IRQ_KEY_ERASED)), + REGMAP_IRQ_REG(GSC_IRQ_EEPROM_WP, 0, BIT(GSC_IRQ_EEPROM_WP)), + REGMAP_IRQ_REG(GSC_IRQ_RESV, 0, BIT(GSC_IRQ_RESV)), + REGMAP_IRQ_REG(GSC_IRQ_GPIO, 0, BIT(GSC_IRQ_GPIO)), + REGMAP_IRQ_REG(GSC_IRQ_TAMPER, 0, BIT(GSC_IRQ_TAMPER)), + REGMAP_IRQ_REG(GSC_IRQ_WDT_TIMEOUT, 0, BIT(GSC_IRQ_WDT_TIMEOUT)), + REGMAP_IRQ_REG(GSC_IRQ_SWITCH_HOLD, 0, BIT(GSC_IRQ_SWITCH_HOLD)), +}; + +static const struct regmap_irq_chip gsc_irq_chip = { + .name = "gateworks-gsc", + .irqs = gsc_irqs, + .num_irqs = ARRAY_SIZE(gsc_irqs), + .num_regs = 1, + .status_base = GSC_IRQ_STATUS, + .mask_base = GSC_IRQ_ENABLE, + .mask_invert = true, + .ack_base = GSC_IRQ_STATUS, + .ack_invert = true, +}; + +static int +gsc_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct gsc_dev *gsc; + int ret; + unsigned int reg; + + gsc = devm_kzalloc(dev, sizeof(*gsc), GFP_KERNEL); + if (!gsc) + return -ENOMEM; + + gsc->dev = &client->dev; + gsc->i2c = client; + gsc->irq = client->irq; + i2c_set_clientdata(client, gsc); + + gsc->regmap = devm_regmap_init(dev, ®map_gsc, client, + &gsc_regmap_config); + if (IS_ERR(gsc->regmap)) + return PTR_ERR(gsc->regmap); + + if (regmap_read(gsc->regmap, GSC_FW_VER, ®)) + return -EIO; + gsc->fwver = reg; + + regmap_read(gsc->regmap, GSC_FW_CRC, ®); + gsc->fwcrc = reg; + regmap_read(gsc->regmap, GSC_FW_CRC + 1, ®); + gsc->fwcrc |= reg << 8; + + gsc->i2c_hwmon = i2c_new_dummy(client->adapter, GSC_HWMON); + if (!gsc->i2c_hwmon) { + dev_err(dev, "Failed to allocate I2C device for HWMON\n"); + return -ENODEV; + } + i2c_set_clientdata(gsc->i2c_hwmon, gsc); + + gsc->regmap_hwmon = devm_regmap_init(dev, ®map_gsc, gsc->i2c_hwmon, + &gsc_regmap_hwmon_config); + if (IS_ERR(gsc->regmap_hwmon)) { + ret = PTR_ERR(gsc->regmap_hwmon); + dev_err(dev, "failed to allocate register map: %d\n", ret); + goto err_regmap; + } + + ret = devm_regmap_add_irq_chip(dev, gsc->regmap, gsc->irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &gsc_irq_chip, &gsc->irq_chip_data); + if (ret) + goto err_regmap; + + dev_info(dev, "Gateworks System Controller v%d: fw 0x%04x\n", + gsc->fwver, gsc->fwcrc); + + ret = sysfs_create_group(&dev->kobj, &attr_group); + if (ret) + dev_err(dev, "failed to create sysfs attrs\n"); + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) + goto err_sysfs; + + return 0; + +err_sysfs: + sysfs_remove_group(&dev->kobj, &attr_group); +err_regmap: + i2c_unregister_device(gsc->i2c_hwmon); + + return ret; +} + +static int gsc_remove(struct i2c_client *client) +{ + struct gsc_dev *gsc = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &attr_group); + i2c_unregister_device(gsc->i2c_hwmon); + + return 0; +} + +static struct i2c_driver gsc_driver = { + .driver = { + .name = "gateworks-gsc", + .of_match_table = of_match_ptr(gsc_of_match), + }, + .probe = gsc_probe, + .remove = gsc_remove, +}; + +module_i2c_driver(gsc_driver); + +MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>"); +MODULE_DESCRIPTION("I2C Core interface for GSC"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/gsc.h b/include/linux/mfd/gsc.h new file mode 100644 index 0000000..dcae61b --- /dev/null +++ b/include/linux/mfd/gsc.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Gateworks Corporation + */ +#ifndef __LINUX_MFD_GSC_H_ +#define __LINUX_MFD_GSC_H_ + +/* Device Addresses */ +#define GSC_MISC 0x20 +#define GSC_UPDATE 0x21 +#define GSC_GPIO 0x23 +#define GSC_HWMON 0x29 +#define GSC_EEPROM0 0x50 +#define GSC_EEPROM1 0x51 +#define GSC_EEPROM2 0x52 +#define GSC_EEPROM3 0x53 +#define GSC_RTC 0x68 + +/* Register offsets */ +#define GSC_CTRL_0 0x00 +#define GSC_CTRL_1 0x01 +#define GSC_TIME 0x02 +#define GSC_TIME_ADD 0x06 +#define GSC_IRQ_STATUS 0x0A +#define GSC_IRQ_ENABLE 0x0B +#define GSC_FW_CRC 0x0C +#define GSC_FW_VER 0x0E +#define GSC_WP 0x0F + +/* Bit definitions */ +#define GSC_CTRL_0_PB_HARD_RESET 0 +#define GSC_CTRL_0_PB_CLEAR_SECURE_KEY 1 +#define GSC_CTRL_0_PB_SOFT_POWER_DOWN 2 +#define GSC_CTRL_0_PB_BOOT_ALTERNATE 3 +#define GSC_CTRL_0_PERFORM_CRC 4 +#define GSC_CTRL_0_TAMPER_DETECT 5 +#define GSC_CTRL_0_SWITCH_HOLD 6 + +#define GSC_CTRL_1_SLEEP_ENABLE 0 +#define GSC_CTRL_1_ACTIVATE_SLEEP 1 +#define GSC_CTRL_1_LATCH_SLEEP_ADD 2 +#define GSC_CTRL_1_SLEEP_NOWAKEPB 3 +#define GSC_CTRL_1_WDT_TIME 4 +#define GSC_CTRL_1_WDT_ENABLE 5 +#define GSC_CTRL_1_SWITCH_BOOT_ENABLE 6 +#define GSC_CTRL_1_SWITCH_BOOT_CLEAR 7 + +#define GSC_IRQ_PB 0 +#define GSC_IRQ_KEY_ERASED 1 +#define GSC_IRQ_EEPROM_WP 2 +#define GSC_IRQ_RESV 3 +#define GSC_IRQ_GPIO 4 +#define GSC_IRQ_TAMPER 5 +#define GSC_IRQ_WDT_TIMEOUT 6 +#define GSC_IRQ_SWITCH_HOLD 7 + +/* Max registers */ +#define GSC_HWMON_MAX_REG 56 + +struct gsc_dev { + struct device *dev; + + struct i2c_client *i2c; /* 0x20: interrupt controller, WDT */ + struct i2c_client *i2c_hwmon; /* 0x29: hwmon, fan controller */ + + struct regmap *regmap; + struct regmap *regmap_hwmon; + struct regmap_irq_chip_data *irq_chip_data; + + int irq; + unsigned int fwver; + unsigned short fwcrc; +}; + +#endif /* __LINUX_MFD_GSC_H_ */
The Gateworks System Controller (GSC) is an I2C slave controller implemented with an MSP430 micro-controller whose firmware embeds the following features: - I/O expander (16 GPIO's) using PCA955x protocol - Real Time Clock using DS1672 protocol - User EEPROM using AT24 protocol - HWMON using custom protocol - Interrupt controller with tamper detect, user pushbotton - Watchdog controller capable of full board power-cycle - Power Control capable of full board power-cycle see http://trac.gateworks.com/wiki/gsc for more details Cc: Randy Dunlap <rdunlap@infradead.org> Signed-off-by: Tim Harvey <tharvey@gateworks.com> --- v3: - rename gsc->gateworks-gsc - remove uncecessary include for linux/mfd/core.h - upercase I2C in comments - remove i2c debug - remove uncecessary comments - don't use KBUILD_MODNAME for name - remove unnecessary v1/v2/v3 tracking - unregister hwmon i2c adapter on remove v2: - change license comment block style - remove COMPILE_TEST (Randy) - fixed whitespace issues - replaced a printk with dev_err --- drivers/mfd/Kconfig | 13 ++ drivers/mfd/Makefile | 1 + drivers/mfd/gateworks-gsc.c | 285 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/gsc.h | 75 ++++++++++++ 4 files changed, 374 insertions(+) create mode 100644 drivers/mfd/gateworks-gsc.c create mode 100644 include/linux/mfd/gsc.h