blob: 95f2a9935fc739bf12df5f42f2178ac6b506ed4b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/**
* Driver for Schumacher (Photeon ps13216 Solid-State Relay)
*
* The driver implementation supports only ps13216 operating under Gang Mode. The IC should be
* pre-configured to Gang Mode by programming the CTRL.GANG eFuse bit during production.
*
* Copyright 2021 Google LLC.
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/ssr/ps13216_regs.h>
#include <linux/workqueue.h>
#include "ps13216.h"
#include "ssr-core.h"
/**
* Misc constants
*/
// It takes 34 ms to go from OFF state to RUN state
#define PS13216_IF_EN_STARTUP_DELAY_MS 34
// It takes at least 128 ms to fill the FIFO memory bank. For some cases such as switch 0,
// however, the first ~30 samples are always invalid (zero value). Hence we keep capture
// 30 ms to overwrite the initial samples.
#define PS13216_HVSENSE_ADC_CAM_DELAY_MS 158
#define PS13216_ADC_FIFO_BANK_BYTES 256
// Delay value for charge pumps. ADC readings
// taken before the charge pumps have fully
// charged will be inaccurate.
#define PS13216_CHARGE_PUMP_DELAY_MS 30
#define DEFAULT_HV_BUCK_DISABLE_DURATION 250
// Default value for duration from user space.
#define DEFAULT_HV_BUCK_DISABLE_DURATION_USER 250
// Each retry attempt for SSR close takes about 32ms. Limit attempts to 10, so
// the maximum delay is ~300ms.
#define PS13216_SSR_CLOSE_MAX_RETRY 10
enum ssr_state {
SSR_OPEN,
SSR_WAITING_TO_CLOSE,
SSR_CLOSED,
};
struct ps13216_device {
struct device *ssr_dev;
struct i2c_client *client;
struct gpio_desc *enable_gpio;
struct gpio_desc *hv_buck_gpio;
enum ssr_state relay_open_state;
struct regmap *regmap;
struct delayed_work enable_hv_buck_work;
u8 adc_fifo_data[PS13216_ADC_FIFO_BANK_BYTES];
int hv_buck_disable_duration;
// True if blocked for SSR use. HV buck not allowed to control from user space.
bool hv_buck_blocked_by_ssr;
int hv_buck_disable_duration_user;
int ssr_close_retry_count;
};
static DEFINE_MUTEX(i2c_adc_lock);
static DEFINE_MUTEX(sysfs_hv_buck_disable_lock);
static DEFINE_MUTEX(hv_buck_lock);
static int ps13216_check_internal_error(struct ps13216_device *ps)
{
u8 global_event;
regmap_bulk_read(ps->regmap, PS13216_REG_GLOBAL_EVENTS, (u8 *)&global_event, 1);
if (global_event &
(PS13216_M_GLOBAL_EVENTS_LV2HV0 | PS13216_M_GLOBAL_EVENTS_LV2HV1)) {
dev_warn(&ps->client->dev,
"internal communication error. global_event reg: %d\n",
global_event);
return -EREMOTEIO;
}
return 0;
}
static void ps13216_reset_events(struct ps13216_device *ps)
{
regmap_write(ps->regmap, PS13216_REG_EVENT0, PS13216_M_EVENT0_ALL);
regmap_write(ps->regmap, PS13216_REG_EVENT1, PS13216_M_EVENT1_ALL);
regmap_write(ps->regmap, PS13216_REG_GLOBAL_EVENTS, PS13216_M_GLOBAL_EVENTS_ALL);
regmap_write(ps->regmap, PS13216_REG_EVENT0_MASK, (u8)~(PS13216_M_EVENT0_MASK_ALL));
regmap_write(ps->regmap, PS13216_REG_EVENT1_MASK, (u8)~(PS13216_M_EVENT1_MASK_ALL));
}
static void ps13216_relay_open(struct ps13216_device *ps, bool relay_open)
{
if (!relay_open && !ps->ssr_close_retry_count) {
dev_err(ps->ssr_dev,
"%s: maximum retries reached. SSR failed to close.",
__func__);
return;
}
if (relay_open) {
regmap_write(ps->regmap, PS13216_REG_BB0XB, PS13216_M_BB0XB_C0);
ps->relay_open_state = SSR_OPEN;
dev_info(ps->ssr_dev, "%s: ssr opened!\n", __func__);
if (ps->hv_buck_gpio) {
mutex_lock(&sysfs_hv_buck_disable_lock);
ps->hv_buck_blocked_by_ssr = true;
mutex_unlock(&sysfs_hv_buck_disable_lock);
mutex_lock(&hv_buck_lock);
cancel_delayed_work_sync(&ps->enable_hv_buck_work);
schedule_delayed_work(&ps->enable_hv_buck_work,
msecs_to_jiffies(ps->hv_buck_disable_duration));
mutex_unlock(&hv_buck_lock);
}
} else {
--ps->ssr_close_retry_count;
// Reset event registers
ps13216_reset_events(ps);
// Enable force active and wait for charge pumps to charge
regmap_write(ps->regmap, PS13216_REG_FORCE_ACTIVE,
PS13216_V_FORCE_ACTIVE(1));
msleep(PS13216_CHARGE_PUMP_DELAY_MS);
// Enter SW test mode
regmap_write(ps->regmap, PS13216_REG_SW_TEST_MODE,
PS13216_ENTER_TEST_MODE_1ST_BYTE);
regmap_write(ps->regmap, PS13216_REG_SW_TEST_MODE,
PS13216_ENTER_TEST_MODE_2ND_BYTE);
// Config Signal Spy on Channel 0 Zero-crossing comparator
regmap_write(ps->regmap, PS13216_REG_TEST_SIGNAL_SPY,
PS13216_SIGNALSPY_ZERO_CROSS_CH0);
// Set Fast Mode control for Channel 0
regmap_write(ps->regmap, PS13216_REG_SETUP,
PS13216_V_SETUP_FAST_MODE_CONFIG(PS13216_SETUP_FAST_MODE_CH0_CH1));
// Relay state must be updated after setting "Signal Spy" on
// zero-crossing comparator in order to avoid handling the
// unwanted interrupt introduced when switching to signal spy
// mode.
ps->relay_open_state = SSR_WAITING_TO_CLOSE;
}
}
static void enable_hv_buck_handler(struct work_struct *work)
{
struct ps13216_device* ps = container_of(
work, struct ps13216_device, enable_hv_buck_work.work);
if (ps->hv_buck_gpio) {
gpiod_set_value(ps->hv_buck_gpio, 0);
mutex_lock(&sysfs_hv_buck_disable_lock);
if (ps->hv_buck_blocked_by_ssr) {
dev_info(ps->ssr_dev, "%s: HV buck enabled", __func__);
ps->hv_buck_blocked_by_ssr = false;
} else {
dev_info(ps->ssr_dev, "%s: HV buck enabled for user", __func__);
}
mutex_unlock(&sysfs_hv_buck_disable_lock);
}
}
static irqreturn_t ps13216_irq_handler_thread(int irq, void *data)
{
struct ps13216_device *ps = data;
struct device *dev = &ps->client->dev;
if (ps->relay_open_state == SSR_WAITING_TO_CLOSE) {
// Close SSR switch
regmap_write(ps->regmap, PS13216_REG_BB1XB, PS13216_M_BB1XB_C0);
ps->relay_open_state = SSR_CLOSED;
// Leave SW test mode
regmap_write(ps->regmap, PS13216_REG_SW_TEST_MODE,
PS13216_LEAVE_TEST_MODE_1ST_BYTE);
regmap_write(ps->regmap, PS13216_REG_SW_TEST_MODE,
PS13216_LEAVE_TEST_MODE_2ND_BYTE);
// Reset signal spy
regmap_write(ps->regmap, PS13216_REG_TEST_SIGNAL_SPY,
PS13216_SIGNALSPY_DISABLE);
if (ps13216_check_internal_error(ps)) {
dev_warn(dev, "%s: ssr failed to close, retrying!\n", __func__);
ps13216_relay_open(ps, false);
return IRQ_HANDLED;
}
dev_info(dev, "%s: ssr closed!\n", __func__);
if (ps->hv_buck_gpio) {
mutex_lock(&sysfs_hv_buck_disable_lock);
ps->hv_buck_blocked_by_ssr = true;
mutex_unlock(&sysfs_hv_buck_disable_lock);
mutex_lock(&hv_buck_lock);
cancel_delayed_work_sync(&ps->enable_hv_buck_work);
gpiod_set_value(ps->hv_buck_gpio, 1);
dev_info(ps->ssr_dev, "%s: HV buck disabled", __func__);
mutex_unlock(&hv_buck_lock);
}
}
return IRQ_HANDLED;
}
static ssize_t ssr_relay_open_store(struct device *dev, const char *buf, size_t count) {
int ret = 0;
struct ps13216_device *ps = dev_get_drvdata(dev);
bool relay_open;
ret = kstrtobool(buf, &relay_open);
if (ret < 0)
return ret;
ps->ssr_close_retry_count = PS13216_SSR_CLOSE_MAX_RETRY;
ps13216_relay_open(ps, relay_open);
return count;
}
static ssize_t ssr_relay_state_show(struct device *dev, char *buf) {
struct ps13216_device *ps = dev_get_drvdata(dev);
if (ps->relay_open_state == SSR_OPEN) {
return scnprintf(buf, PAGE_SIZE, "open\n");
} else if (ps->relay_open_state == SSR_WAITING_TO_CLOSE) {
return scnprintf(buf, PAGE_SIZE, "closing\n");
} else { // SSR_CLOSED
return scnprintf(buf, PAGE_SIZE, "closed\n");
}
}
/**
* ssr_voltage_show() - Read the voltage from a specific channel
* @dev: The device structure of SSR
* @channel: The bit of the channel to be read
* @buf: The buffer for the voltage data
*
* Return: The number of characters written into buf.
*/
static ssize_t ssr_voltage_show(struct device *dev, unsigned int channel, char *buf) {
struct ps13216_device *ps = dev_get_drvdata(dev);
u16 fifo_len;
u8 *fifo_data_ptr;
size_t sample_count;
size_t invalid_sample_count;
size_t idx;
int raw_voltage;
int mean_squared_voltage;
int ret;
mutex_lock(&i2c_adc_lock);
// Clear ADC EOC (End of Conversion) event flag.
regmap_write(ps->regmap, PS13216_REG_EVENT1, PS13216_M_EVENT1_EOC);
regmap_write(ps->regmap, PS13216_REG_ADC_CTRL,
PS13216_V_ADC_CTRL_MODE(PS13216_ADC_MODE_CAM) | channel);
msleep(PS13216_HVSENSE_ADC_CAM_DELAY_MS);
regmap_write(ps->regmap, PS13216_REG_ADC_CTRL, 0x00);
// Read FIFO length
regmap_bulk_read(ps->regmap, PS13216_REG_ADC_FIFO_FILL_CNT_LSB, (u8 *)&fifo_len, 2);
ret = regmap_noinc_read(ps->regmap, PS13216_REG_ADC_FIFO, ps->adc_fifo_data, fifo_len);
mutex_unlock(&i2c_adc_lock);
if (ret < 0)
return ret;
// Each voltage read takes 2 bytes
sample_count = fifo_len / 2;
fifo_data_ptr = ps->adc_fifo_data;
mean_squared_voltage = 0;
invalid_sample_count = 0;
for (idx = 0; idx < sample_count; fifo_data_ptr += 2, ++idx) {
// Only 10 bits are required to decode the voltage data.
raw_voltage = (int)((fifo_data_ptr[1] & 3) << 8) | fifo_data_ptr[0];
// Due to unknown reasons, the driver might return some empty values (0).
// Skip then when calculating mean squared value.
if (raw_voltage == 0) {
invalid_sample_count++;
continue;
}
// For switch voltage monitoring, should substract 512 from the raw value.
if (channel == PS13216_M_ADC_CTRL_VI0 || channel == PS13216_M_ADC_CTRL_VI1)
raw_voltage -= 512;
mean_squared_voltage += raw_voltage * raw_voltage;
}
if (invalid_sample_count > 0)
dev_warn(dev, "%s: found %d invalid samples", __func__, invalid_sample_count);
if (sample_count == invalid_sample_count) {
dev_err(dev, "%s: no valid samples collected", __func__);
return scnprintf(buf, PAGE_SIZE, "0\n");
}
mean_squared_voltage /= (sample_count - invalid_sample_count);
return scnprintf(buf, PAGE_SIZE, "%d\n", mean_squared_voltage);
}
static ssize_t ssr_hvsense_voltage_show(struct device *dev, char *buf) {
return ssr_voltage_show(dev, PS13216_M_ADC_CTRL_HV_SENSE, buf);
}
static ssize_t ssr_acin1_voltage_show(struct device *dev, char *buf) {
return ssr_voltage_show(dev, PS13216_M_ADC_CTRL_VI0, buf);
}
static ssize_t ssr_acin2_voltage_show(struct device *dev, char *buf) {
return ssr_voltage_show(dev, PS13216_M_ADC_CTRL_VI1, buf);
}
static const struct ssr_ops ssr_ops = {
.relay_open_store = ssr_relay_open_store,
.relay_state_show = ssr_relay_state_show,
.hv_vdd_show = ssr_hvsense_voltage_show,
.acin1_vdd_show = ssr_acin1_voltage_show,
.acin2_vdd_show = ssr_acin2_voltage_show,
};
static ssize_t hv_buck_duration_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ps13216_device *ps = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%d\n", ps->hv_buck_disable_duration);
}
static ssize_t hv_buck_duration_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct ps13216_device *ps = dev_get_drvdata(dev->parent);
int duration;
if (kstrtoint(buf, 0, &duration))
return -EINVAL;
ps->hv_buck_disable_duration = duration;
return len;
}
static DEVICE_ATTR(hv_buck_duration, 0644,
hv_buck_duration_show, hv_buck_duration_store);
static ssize_t hv_buck_disable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ps13216_device *ps = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%d\n", ps->hv_buck_disable_duration_user);
}
static ssize_t hv_buck_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
int ret;
int hv_buck_disable_duration_user;
struct ps13216_device *ps = dev_get_drvdata(dev->parent);
if (!ps->hv_buck_gpio)
return -EIO;
mutex_lock(&sysfs_hv_buck_disable_lock);
if (ps->hv_buck_blocked_by_ssr) {
mutex_unlock(&sysfs_hv_buck_disable_lock);
return -EBUSY;
}
ret = kstrtoint(buf, 0, &hv_buck_disable_duration_user);
if (ret < 0 || hv_buck_disable_duration_user <= 0) {
mutex_unlock(&sysfs_hv_buck_disable_lock);
return -EINVAL;
}
// Disable HV buck now.
gpiod_set_value(ps->hv_buck_gpio, 1);
dev_info(ps->ssr_dev, "%s: HV buck disabled by user", __func__);
// Delayed work to re-enable HV buck.
ps->hv_buck_disable_duration_user = hv_buck_disable_duration_user;
schedule_delayed_work(&ps->enable_hv_buck_work,
msecs_to_jiffies(ps->hv_buck_disable_duration_user));
mutex_unlock(&sysfs_hv_buck_disable_lock);
return len;
}
static DEVICE_ATTR_RW(hv_buck_disable);
static struct attribute *ps13216_attributes[] = {
&dev_attr_hv_buck_duration.attr,
&dev_attr_hv_buck_disable.attr,
NULL,
};
static struct attribute_group ps13216_attr_group = {
.attrs = ps13216_attributes
};
static int ps13216_probe(struct i2c_client *client, const struct i2c_device_id *id) {
struct device *dev = &client->dev;
struct ps13216_device *ps;
int ret;
ps = devm_kmalloc(dev, sizeof(struct ps13216_device), GFP_KERNEL);
if (!ps) {
ret = -ENOMEM;
goto error;
}
ps->client = client;
ps->relay_open_state = SSR_OPEN;
ps->hv_buck_disable_duration = DEFAULT_HV_BUCK_DISABLE_DURATION;
ps->hv_buck_blocked_by_ssr = false;
ps->hv_buck_disable_duration_user = DEFAULT_HV_BUCK_DISABLE_DURATION_USER;
if (client->irq) {
ret = devm_request_threaded_irq(dev, client->irq, NULL,
ps13216_irq_handler_thread,
IRQF_ONESHOT,
dev_name(dev), ps);
if (ret < 0) {
dev_err(dev, "get irq fail: %d\n", ret);
return ret;
}
}
// Initialize SSR_EN gpio and enable Schumacher
ps->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
if (IS_ERR(ps->enable_gpio)) {
dev_err(dev, "%s: failed to initialize enable_gpio.\n", __func__);
ret = PTR_ERR(ps->enable_gpio);
goto error;
}
msleep(PS13216_IF_EN_STARTUP_DELAY_MS);
ps->hv_buck_gpio = devm_gpiod_get(dev, "hvbuck", GPIOD_OUT_LOW);
if (IS_ERR(ps->hv_buck_gpio)) {
dev_err(dev, "%s: failed to initialize hv_buck_gpio, ignored\n", __func__);
ps->hv_buck_gpio = NULL;
} else {
gpiod_direction_output(ps->hv_buck_gpio, 0);
}
i2c_set_clientdata(client, ps);
ps->regmap = devm_regmap_init_i2c(client, &ps13216_regmap_config);
if (IS_ERR(ps->regmap)) {
ret = PTR_ERR(ps->regmap);
dev_err(dev, "%s: failed to allocate register map: %d\n", __func__, ret);
goto error;
}
ps->ssr_dev = ssr_device_register(dev, &ssr_ops);
if (IS_ERR(ps->ssr_dev)) {
ret = PTR_ERR(ps->ssr_dev);
goto error;
}
dev_info(dev, "%s: %s probe successfully!\n", __func__, client->name);
ret = sysfs_create_group(&ps->ssr_dev->kobj, &ps13216_attr_group);
if (ret) {
dev_err(dev, "ssr sysfs rc: %d\n", ret);
goto error;
}
INIT_DELAYED_WORK(&ps->enable_hv_buck_work, enable_hv_buck_handler);
return 0;
error:
dev_err(dev, "%s: error %d\n", __func__, ret);
return ret;
}
static int ps13216_remove(struct i2c_client *client) {
struct ps13216_device *ps = i2c_get_clientdata(client);
ssr_device_unregister(ps->ssr_dev);
return 0;
}
static const struct i2c_device_id ps13216_id[] = {
{ "ps13216-ssr", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ps13216_id);
static struct of_device_id ps13216_of_match[] = {
{
.compatible = "photeon,ps13216",
},
{ },
};
MODULE_DEVICE_TABLE(of, ps13216_of_match);
static struct i2c_driver ps13216_driver = {
.driver = {
.name = "ps13216-ssr",
.of_match_table = ps13216_of_match,
},
.probe = ps13216_probe,
.remove = ps13216_remove,
.id_table = ps13216_id,
};
module_i2c_driver(ps13216_driver);
MODULE_AUTHOR("Paul Wang <tzupengwang@google.com>");
MODULE_DESCRIPTION("PS13216 SSR driver");
MODULE_LICENSE("GPL v2");