Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/mfd/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 6 kB image not shown  

Quelle  gateworks-gsc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * The Gateworks System Controller (GSC) is a multi-function
 * device designed for use in Gateworks Single Board Computers.
 * The control interface is I2C, with an interrupt. The device supports
 * system functions such as push-button monitoring, multiple ADC's for
 * voltage and temperature monitoring, fan controller and watchdog monitor.
 *
 * Copyright (C) 2020 Gateworks Corporation
 */


#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>

#include <linux/unaligned.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

int gsc_write(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;
}
EXPORT_SYMBOL_GPL(gsc_write);

int gsc_read(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;
}
EXPORT_SYMBOL_GPL(gsc_read);

/*
 * 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);

 put_unaligned_le32(secs, regs);
 ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4);
 if (ret)
  return ret;

 ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
     BIT(GSC_CTRL_1_SLEEP_ADD),
     BIT(GSC_CTRL_1_SLEEP_ADD));
 if (ret)
  return ret;

 ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
     BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
     BIT(GSC_CTRL_1_SLEEP_ENABLE),
     BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
     BIT(GSC_CTRL_1_SLEEP_ENABLE));


 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);
 else
  dev_err(dev, "invalid command: '%s'\n", name);

 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;
 long value;

 if (strcasecmp(name, "powerdown") == 0) {
  if (kstrtol(buf, 0, &value) == 0)
   gsc_powerdown(gsc, value);
 } else {
  dev_err(dev, "invalid command: '%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", },
 { }
};
MODULE_DEVICE_TABLE(of, gsc_of_match);

static const struct regmap_bus gsc_regmap_bus = {
 .reg_read = gsc_read,
 .reg_write = gsc_write,
};

static const struct regmap_config gsc_regmap_config = {
 .reg_bits = 8,
 .val_bits = 8,
 .cache_type = REGCACHE_NONE,
 .max_register = GSC_WP,
};

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,
 .unmask_base = GSC_IRQ_ENABLE,
 .ack_base = GSC_IRQ_STATUS,
 .ack_invert = true,
};

static int gsc_probe(struct i2c_client *client)
{
 struct device *dev = &client->dev;
 struct gsc_dev *gsc;
 struct regmap_irq_chip_data *irq_data;
 int ret;
 unsigned int reg;

 gsc = devm_kzalloc(dev, sizeof(*gsc), GFP_KERNEL);
 if (!gsc)
  return -ENOMEM;

 gsc->dev = &client->dev;
 gsc->i2c = client;
 i2c_set_clientdata(client, gsc);

 gsc->regmap = devm_regmap_init(dev, &gsc_regmap_bus, 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 = devm_i2c_new_dummy_device(dev, client->adapter,
         GSC_HWMON);
 if (IS_ERR(gsc->i2c_hwmon)) {
  dev_err(dev, "Failed to allocate I2C device for HWMON\n");
  return PTR_ERR(gsc->i2c_hwmon);
 }

 ret = devm_regmap_add_irq_chip(dev, gsc->regmap, client->irq,
           IRQF_ONESHOT | IRQF_SHARED |
           IRQF_TRIGGER_LOW, 0,
           &gsc_irq_chip, &irq_data);
 if (ret)
  return ret;

 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 = devm_of_platform_populate(dev);
 if (ret) {
  sysfs_remove_group(&dev->kobj, &attr_group);
  return ret;
 }

 return 0;
}

static void gsc_remove(struct i2c_client *client)
{
 sysfs_remove_group(&client->dev.kobj, &attr_group);
}

static struct i2c_driver gsc_driver = {
 .driver = {
  .name = "gateworks-gsc",
  .of_match_table = gsc_of_match,
 },
 .probe  = gsc_probe,
 .remove  = gsc_remove,
};
module_i2c_driver(gsc_driver);

MODULE_AUTHOR("Tim Harvey ");
MODULE_DESCRIPTION("I2C Core interface for GSC");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=97 H=94 G=95

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.