Skip to content

Commit d852349

Browse files
GateworksLee Jones
authored andcommitted
mfd: Add Gateworks System Controller core driver
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 Signed-off-by: Tim Harvey <[email protected]> Signed-off-by: Lee Jones <[email protected]>
1 parent 3e6cae8 commit d852349

File tree

5 files changed

+377
-0
lines changed

5 files changed

+377
-0
lines changed

MAINTAINERS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7028,6 +7028,14 @@ F: kernel/futex.c
70287028
F: tools/perf/bench/futex*
70297029
F: tools/testing/selftests/futex/
70307030

7031+
GATEWORKS SYSTEM CONTROLLER (GSC) DRIVER
7032+
M: Tim Harvey <[email protected]>
7033+
M: Robert Jones <[email protected]>
7034+
S: Maintained
7035+
F: Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml
7036+
F: drivers/mfd/gateworks-gsc.c
7037+
F: include/linux/mfd/gsc.h
7038+
70317039
GASKET DRIVER FRAMEWORK
70327040
M: Rob Springer <[email protected]>
70337041
M: Todd Poynor <[email protected]>

drivers/mfd/Kconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,21 @@ config MFD_EXYNOS_LPASS
407407
Select this option to enable support for Samsung Exynos Low Power
408408
Audio Subsystem.
409409

410+
config MFD_GATEWORKS_GSC
411+
tristate "Gateworks System Controller"
412+
depends on (I2C && OF)
413+
select MFD_CORE
414+
select REGMAP_I2C
415+
select REGMAP_IRQ
416+
help
417+
Enable support for the Gateworks System Controller (GSC) found
418+
on Gateworks Single Board Computers supporting system functions
419+
such as push-button monitor, multiple ADC's for voltage and
420+
temperature monitoring, fan controller and watchdog monitor.
421+
This driver provides common support for accessing the device.
422+
Additional drivers must be enabled in order to use the
423+
functionality of the device.
424+
410425
config MFD_MC13XXX
411426
tristate
412427
depends on (SPI_MASTER || I2C)

drivers/mfd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
1515
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
1616
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o
1717
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
18+
obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o
1819

1920
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
2021
obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o

drivers/mfd/gateworks-gsc.c

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* The Gateworks System Controller (GSC) is a multi-function
4+
* device designed for use in Gateworks Single Board Computers.
5+
* The control interface is I2C, with an interrupt. The device supports
6+
* system functions such as push-button monitoring, multiple ADC's for
7+
* voltage and temperature monitoring, fan controller and watchdog monitor.
8+
*
9+
* Copyright (C) 2020 Gateworks Corporation
10+
*/
11+
12+
#include <linux/device.h>
13+
#include <linux/i2c.h>
14+
#include <linux/interrupt.h>
15+
#include <linux/mfd/gsc.h>
16+
#include <linux/module.h>
17+
#include <linux/mutex.h>
18+
#include <linux/of.h>
19+
#include <linux/of_platform.h>
20+
#include <linux/platform_device.h>
21+
#include <linux/regmap.h>
22+
23+
#include <asm/unaligned.h>
24+
25+
/*
26+
* The GSC suffers from an errata where occasionally during
27+
* ADC cycles the chip can NAK I2C transactions. To ensure we have reliable
28+
* register access we place retries around register access.
29+
*/
30+
#define I2C_RETRIES 3
31+
32+
int gsc_write(void *context, unsigned int reg, unsigned int val)
33+
{
34+
struct i2c_client *client = context;
35+
int retry, ret;
36+
37+
for (retry = 0; retry < I2C_RETRIES; retry++) {
38+
ret = i2c_smbus_write_byte_data(client, reg, val);
39+
/*
40+
* -EAGAIN returned when the i2c host controller is busy
41+
* -EIO returned when i2c device is busy
42+
*/
43+
if (ret != -EAGAIN && ret != -EIO)
44+
break;
45+
}
46+
47+
return 0;
48+
}
49+
EXPORT_SYMBOL_GPL(gsc_write);
50+
51+
int gsc_read(void *context, unsigned int reg, unsigned int *val)
52+
{
53+
struct i2c_client *client = context;
54+
int retry, ret;
55+
56+
for (retry = 0; retry < I2C_RETRIES; retry++) {
57+
ret = i2c_smbus_read_byte_data(client, reg);
58+
/*
59+
* -EAGAIN returned when the i2c host controller is busy
60+
* -EIO returned when i2c device is busy
61+
*/
62+
if (ret != -EAGAIN && ret != -EIO)
63+
break;
64+
}
65+
*val = ret & 0xff;
66+
67+
return 0;
68+
}
69+
EXPORT_SYMBOL_GPL(gsc_read);
70+
71+
/*
72+
* gsc_powerdown - API to use GSC to power down board for a specific time
73+
*
74+
* secs - number of seconds to remain powered off
75+
*/
76+
static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs)
77+
{
78+
int ret;
79+
unsigned char regs[4];
80+
81+
dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n",
82+
secs);
83+
84+
put_unaligned_le32(secs, regs);
85+
ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4);
86+
if (ret)
87+
return ret;
88+
89+
ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
90+
BIT(GSC_CTRL_1_SLEEP_ADD),
91+
BIT(GSC_CTRL_1_SLEEP_ADD));
92+
if (ret)
93+
return ret;
94+
95+
ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
96+
BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
97+
BIT(GSC_CTRL_1_SLEEP_ENABLE),
98+
BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
99+
BIT(GSC_CTRL_1_SLEEP_ENABLE));
100+
101+
102+
return ret;
103+
}
104+
105+
static ssize_t gsc_show(struct device *dev, struct device_attribute *attr,
106+
char *buf)
107+
{
108+
struct gsc_dev *gsc = dev_get_drvdata(dev);
109+
const char *name = attr->attr.name;
110+
int rz = 0;
111+
112+
if (strcasecmp(name, "fw_version") == 0)
113+
rz = sprintf(buf, "%d\n", gsc->fwver);
114+
else if (strcasecmp(name, "fw_crc") == 0)
115+
rz = sprintf(buf, "0x%04x\n", gsc->fwcrc);
116+
else
117+
dev_err(dev, "invalid command: '%s'\n", name);
118+
119+
return rz;
120+
}
121+
122+
static ssize_t gsc_store(struct device *dev, struct device_attribute *attr,
123+
const char *buf, size_t count)
124+
{
125+
struct gsc_dev *gsc = dev_get_drvdata(dev);
126+
const char *name = attr->attr.name;
127+
long value;
128+
129+
if (strcasecmp(name, "powerdown") == 0) {
130+
if (kstrtol(buf, 0, &value) == 0)
131+
gsc_powerdown(gsc, value);
132+
} else {
133+
dev_err(dev, "invalid command: '%s\n", name);
134+
}
135+
136+
return count;
137+
}
138+
139+
static struct device_attribute attr_fwver =
140+
__ATTR(fw_version, 0440, gsc_show, NULL);
141+
static struct device_attribute attr_fwcrc =
142+
__ATTR(fw_crc, 0440, gsc_show, NULL);
143+
static struct device_attribute attr_pwrdown =
144+
__ATTR(powerdown, 0220, NULL, gsc_store);
145+
146+
static struct attribute *gsc_attrs[] = {
147+
&attr_fwver.attr,
148+
&attr_fwcrc.attr,
149+
&attr_pwrdown.attr,
150+
NULL,
151+
};
152+
153+
static struct attribute_group attr_group = {
154+
.attrs = gsc_attrs,
155+
};
156+
157+
static const struct of_device_id gsc_of_match[] = {
158+
{ .compatible = "gw,gsc", },
159+
{ }
160+
};
161+
MODULE_DEVICE_TABLE(of, gsc_of_match);
162+
163+
static struct regmap_bus gsc_regmap_bus = {
164+
.reg_read = gsc_read,
165+
.reg_write = gsc_write,
166+
};
167+
168+
static const struct regmap_config gsc_regmap_config = {
169+
.reg_bits = 8,
170+
.val_bits = 8,
171+
.cache_type = REGCACHE_NONE,
172+
.max_register = GSC_WP,
173+
};
174+
175+
static const struct regmap_irq gsc_irqs[] = {
176+
REGMAP_IRQ_REG(GSC_IRQ_PB, 0, BIT(GSC_IRQ_PB)),
177+
REGMAP_IRQ_REG(GSC_IRQ_KEY_ERASED, 0, BIT(GSC_IRQ_KEY_ERASED)),
178+
REGMAP_IRQ_REG(GSC_IRQ_EEPROM_WP, 0, BIT(GSC_IRQ_EEPROM_WP)),
179+
REGMAP_IRQ_REG(GSC_IRQ_RESV, 0, BIT(GSC_IRQ_RESV)),
180+
REGMAP_IRQ_REG(GSC_IRQ_GPIO, 0, BIT(GSC_IRQ_GPIO)),
181+
REGMAP_IRQ_REG(GSC_IRQ_TAMPER, 0, BIT(GSC_IRQ_TAMPER)),
182+
REGMAP_IRQ_REG(GSC_IRQ_WDT_TIMEOUT, 0, BIT(GSC_IRQ_WDT_TIMEOUT)),
183+
REGMAP_IRQ_REG(GSC_IRQ_SWITCH_HOLD, 0, BIT(GSC_IRQ_SWITCH_HOLD)),
184+
};
185+
186+
static const struct regmap_irq_chip gsc_irq_chip = {
187+
.name = "gateworks-gsc",
188+
.irqs = gsc_irqs,
189+
.num_irqs = ARRAY_SIZE(gsc_irqs),
190+
.num_regs = 1,
191+
.status_base = GSC_IRQ_STATUS,
192+
.mask_base = GSC_IRQ_ENABLE,
193+
.mask_invert = true,
194+
.ack_base = GSC_IRQ_STATUS,
195+
.ack_invert = true,
196+
};
197+
198+
static int gsc_probe(struct i2c_client *client)
199+
{
200+
struct device *dev = &client->dev;
201+
struct gsc_dev *gsc;
202+
struct regmap_irq_chip_data *irq_data;
203+
int ret;
204+
unsigned int reg;
205+
206+
gsc = devm_kzalloc(dev, sizeof(*gsc), GFP_KERNEL);
207+
if (!gsc)
208+
return -ENOMEM;
209+
210+
gsc->dev = &client->dev;
211+
gsc->i2c = client;
212+
i2c_set_clientdata(client, gsc);
213+
214+
gsc->regmap = devm_regmap_init(dev, &gsc_regmap_bus, client,
215+
&gsc_regmap_config);
216+
if (IS_ERR(gsc->regmap))
217+
return PTR_ERR(gsc->regmap);
218+
219+
if (regmap_read(gsc->regmap, GSC_FW_VER, &reg))
220+
return -EIO;
221+
gsc->fwver = reg;
222+
223+
regmap_read(gsc->regmap, GSC_FW_CRC, &reg);
224+
gsc->fwcrc = reg;
225+
regmap_read(gsc->regmap, GSC_FW_CRC + 1, &reg);
226+
gsc->fwcrc |= reg << 8;
227+
228+
gsc->i2c_hwmon = devm_i2c_new_dummy_device(dev, client->adapter,
229+
GSC_HWMON);
230+
if (IS_ERR(gsc->i2c_hwmon)) {
231+
dev_err(dev, "Failed to allocate I2C device for HWMON\n");
232+
return PTR_ERR(gsc->i2c_hwmon);
233+
}
234+
235+
ret = devm_regmap_add_irq_chip(dev, gsc->regmap, client->irq,
236+
IRQF_ONESHOT | IRQF_SHARED |
237+
IRQF_TRIGGER_FALLING, 0,
238+
&gsc_irq_chip, &irq_data);
239+
if (ret)
240+
return ret;
241+
242+
dev_info(dev, "Gateworks System Controller v%d: fw 0x%04x\n",
243+
gsc->fwver, gsc->fwcrc);
244+
245+
ret = sysfs_create_group(&dev->kobj, &attr_group);
246+
if (ret)
247+
dev_err(dev, "failed to create sysfs attrs\n");
248+
249+
ret = devm_of_platform_populate(dev);
250+
if (ret) {
251+
sysfs_remove_group(&dev->kobj, &attr_group);
252+
return ret;
253+
}
254+
255+
return 0;
256+
}
257+
258+
static int gsc_remove(struct i2c_client *client)
259+
{
260+
sysfs_remove_group(&client->dev.kobj, &attr_group);
261+
262+
return 0;
263+
}
264+
265+
static struct i2c_driver gsc_driver = {
266+
.driver = {
267+
.name = "gateworks-gsc",
268+
.of_match_table = gsc_of_match,
269+
},
270+
.probe_new = gsc_probe,
271+
.remove = gsc_remove,
272+
};
273+
module_i2c_driver(gsc_driver);
274+
275+
MODULE_AUTHOR("Tim Harvey <[email protected]>");
276+
MODULE_DESCRIPTION("I2C Core interface for GSC");
277+
MODULE_LICENSE("GPL v2");

include/linux/mfd/gsc.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/* SPDX-License-Identifier: GPL-2.0
2+
*
3+
* Copyright (C) 2020 Gateworks Corporation
4+
*/
5+
#ifndef __LINUX_MFD_GSC_H_
6+
#define __LINUX_MFD_GSC_H_
7+
8+
#include <linux/regmap.h>
9+
10+
/* Device Addresses */
11+
#define GSC_MISC 0x20
12+
#define GSC_UPDATE 0x21
13+
#define GSC_GPIO 0x23
14+
#define GSC_HWMON 0x29
15+
#define GSC_EEPROM0 0x50
16+
#define GSC_EEPROM1 0x51
17+
#define GSC_EEPROM2 0x52
18+
#define GSC_EEPROM3 0x53
19+
#define GSC_RTC 0x68
20+
21+
/* Register offsets */
22+
enum {
23+
GSC_CTRL_0 = 0x00,
24+
GSC_CTRL_1 = 0x01,
25+
GSC_TIME = 0x02,
26+
GSC_TIME_ADD = 0x06,
27+
GSC_IRQ_STATUS = 0x0A,
28+
GSC_IRQ_ENABLE = 0x0B,
29+
GSC_FW_CRC = 0x0C,
30+
GSC_FW_VER = 0x0E,
31+
GSC_WP = 0x0F,
32+
};
33+
34+
/* Bit definitions */
35+
#define GSC_CTRL_0_PB_HARD_RESET 0
36+
#define GSC_CTRL_0_PB_CLEAR_SECURE_KEY 1
37+
#define GSC_CTRL_0_PB_SOFT_POWER_DOWN 2
38+
#define GSC_CTRL_0_PB_BOOT_ALTERNATE 3
39+
#define GSC_CTRL_0_PERFORM_CRC 4
40+
#define GSC_CTRL_0_TAMPER_DETECT 5
41+
#define GSC_CTRL_0_SWITCH_HOLD 6
42+
43+
#define GSC_CTRL_1_SLEEP_ENABLE 0
44+
#define GSC_CTRL_1_SLEEP_ACTIVATE 1
45+
#define GSC_CTRL_1_SLEEP_ADD 2
46+
#define GSC_CTRL_1_SLEEP_NOWAKEPB 3
47+
#define GSC_CTRL_1_WDT_TIME 4
48+
#define GSC_CTRL_1_WDT_ENABLE 5
49+
#define GSC_CTRL_1_SWITCH_BOOT_ENABLE 6
50+
#define GSC_CTRL_1_SWITCH_BOOT_CLEAR 7
51+
52+
#define GSC_IRQ_PB 0
53+
#define GSC_IRQ_KEY_ERASED 1
54+
#define GSC_IRQ_EEPROM_WP 2
55+
#define GSC_IRQ_RESV 3
56+
#define GSC_IRQ_GPIO 4
57+
#define GSC_IRQ_TAMPER 5
58+
#define GSC_IRQ_WDT_TIMEOUT 6
59+
#define GSC_IRQ_SWITCH_HOLD 7
60+
61+
int gsc_read(void *context, unsigned int reg, unsigned int *val);
62+
int gsc_write(void *context, unsigned int reg, unsigned int val);
63+
64+
struct gsc_dev {
65+
struct device *dev;
66+
67+
struct i2c_client *i2c; /* 0x20: interrupt controller, WDT */
68+
struct i2c_client *i2c_hwmon; /* 0x29: hwmon, fan controller */
69+
70+
struct regmap *regmap;
71+
72+
unsigned int fwver;
73+
unsigned short fwcrc;
74+
};
75+
76+
#endif /* __LINUX_MFD_GSC_H_ */

0 commit comments

Comments
 (0)