* [rtc-linux] Re: [PATCH v3 1/4] mfd: cros_ec: Get rid of cros_ec_check_features from cros_ec_dev.
From: 'Benson Leung' via rtc-linux @ 2017-07-17 16:37 UTC (permalink / raw)
To: Enric Balletbo Serra
Cc: Gwendal Grignou, Enric Balletbo i Serra, Jonathan Cameron,
Lee Jones, Benson Leung, Javier Martinez Canillas, Guenter Roeck,
Linux Kernel, linux-iio, rtc-linux
In-Reply-To: <CAFqH_53O8UDPnhCq7BwS7tZ9814HD5GLXWGfbeYJ2PFFk06Uzg@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1318 bytes --]
Hi Enric,
On Mon, Jul 17, 2017 at 12:30:30PM +0200, Enric Balletbo Serra wrote:
> Hi Gwendal,
>
> Interesting I didn't know that. So are you saying that this patch will
> break support for devices like Pixel 2? I tested the patches on
> various devices but not on Pixel 2 so could be.
This would affect any systems where we have chained ECs. Samus (Chromebook
Pixel 2015) has its PD EC hooked up in this way. Practically speaking,
that would mean that the cros_usbpd-charger power supply driver may not work
correctly.
I don't think we expect any other systems to have this configuration,
but the first one we shipped did, so we will have to support it.
Thanks,
Benson
--
Benson Leung
Staff Software Engineer
Chrome OS Kernel
Google Inc.
bleung@google.com
Chromium OS Project
bleung@chromium.org
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* [rtc-linux] Re: [PATCH v3 1/4] mfd: cros_ec: Get rid of cros_ec_check_features from cros_ec_dev.
From: Enric Balletbo Serra @ 2017-07-17 10:30 UTC (permalink / raw)
To: Gwendal Grignou
Cc: Enric Balletbo i Serra, Jonathan Cameron, Lee Jones, Benson Leung,
Javier Martinez Canillas, Guenter Roeck, Linux Kernel, linux-iio,
rtc-linux
In-Reply-To: <CAMHSBOUT2hpcXrSuvRrZfnEXWtN9QfNLqHvM4TCvHPKiokP0Ag@mail.gmail.com>
Hi Gwendal,
2017-07-13 22:33 GMT+02:00 Gwendal Grignou <gwendal@chromium.org>:
> On Wed, Jul 12, 2017 at 3:13 AM, Enric Balletbo i Serra
> <enric.balletbo@collabora.com> wrote:
>> The cros_ec_dev driver should be used only to expose the Chrome OS Embedded
>> Controller to user-space and should not be used to add MFD devices by
>> calling mfd_add_devices. This patch moves this logic to the MFD cros_ec
>> driver and removes the MFD bits from the character device driver. Also
>> makes independent the IIO driver from the character device as also has no
>> sense.
>
> cros_ec_dev serves another purpose: it allows to represent an EC that
> does not have a cros_ec structure. It happens when there are several
> EC in a chromebook, and one EC is connected through another EC.
> One example is Samus (Pixel 2): where we have:
>
> (main SOC, Application Processor) AP --> (main Embedded Controller) EC
> ---> (Power Delivery [PD}) EC
>
> We access to the PD EC via pass-through commands through the main EC.
> Each EC has a cros_ec_dev structure, but only the main EC as a
> cros_ec_device structure (I will forever regret the structure names).
>
> Now form the AP point of view, both ECs use the same protocol. That
> why the sensors and other devcies that are registered by looking at
> the feature fields are registered with cros_ec_dev as their parent.
> Other devices that are registered from the device tree, predating the
> feature field support, are registered with cros_ec_device as their
> parent.
>
Interesting I didn't know that. So are you saying that this patch will
break support for devices like Pixel 2? I tested the patches on
various devices but not on Pixel 2 so could be.
Thanks,
Enric
> Gwendal.
>
>>
>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>> Acked-by: Jonathan Cameron <jic23@kernel.org>
>> ---
>> .../iio/common/cros_ec_sensors/cros_ec_sensors.c | 8 -
>> .../common/cros_ec_sensors/cros_ec_sensors_core.c | 8 +-
>> drivers/iio/light/cros_ec_light_prox.c | 8 -
>> drivers/iio/pressure/cros_ec_baro.c | 8 -
>> drivers/mfd/cros_ec.c | 160 ++++++++++++++++++++
>> drivers/platform/chrome/cros_ec_dev.c | 161 ---------------------
>> include/linux/mfd/cros_ec.h | 6 +-
>> 7 files changed, 170 insertions(+), 189 deletions(-)
>>
>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
>> index 38e8783..9b53a01 100644
>> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
>> @@ -191,19 +191,11 @@ static const struct iio_info ec_sensors_info = {
>> static int cros_ec_sensors_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
>> - struct cros_ec_device *ec_device;
>> struct iio_dev *indio_dev;
>> struct cros_ec_sensors_state *state;
>> struct iio_chan_spec *channel;
>> int ret, i;
>>
>> - if (!ec_dev || !ec_dev->ec_dev) {
>> - dev_warn(&pdev->dev, "No CROS EC device found.\n");
>> - return -EINVAL;
>> - }
>> - ec_device = ec_dev->ec_dev;
>> -
>> indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
>> if (!indio_dev)
>> return -ENOMEM;
>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>> index 416cae5..0cdb64a 100644
>> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>> @@ -41,12 +41,13 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>> {
>> struct device *dev = &pdev->dev;
>> struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
>> - struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
>> + struct cros_ec_device *ec_dev = dev_get_drvdata(pdev->dev.parent);
>> struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
>>
>> platform_set_drvdata(pdev, indio_dev);
>>
>> - state->ec = ec->ec_dev;
>> + state->ec = ec_dev;
>> +
>> state->msg = devm_kzalloc(&pdev->dev,
>> max((u16)sizeof(struct ec_params_motion_sense),
>> state->ec->max_response), GFP_KERNEL);
>> @@ -59,7 +60,8 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>>
>> /* Set up the host command structure. */
>> state->msg->version = 2;
>> - state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
>> + state->msg->command = EC_CMD_MOTION_SENSE_CMD +
>> + sensor_platform->cmd_offset;
>> state->msg->outsize = sizeof(struct ec_params_motion_sense);
>>
>> indio_dev->dev.parent = &pdev->dev;
>> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
>> index 7217223..2133ddc 100644
>> --- a/drivers/iio/light/cros_ec_light_prox.c
>> +++ b/drivers/iio/light/cros_ec_light_prox.c
>> @@ -181,19 +181,11 @@ static const struct iio_info cros_ec_light_prox_info = {
>> static int cros_ec_light_prox_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
>> - struct cros_ec_device *ec_device;
>> struct iio_dev *indio_dev;
>> struct cros_ec_light_prox_state *state;
>> struct iio_chan_spec *channel;
>> int ret;
>>
>> - if (!ec_dev || !ec_dev->ec_dev) {
>> - dev_warn(dev, "No CROS EC device found.\n");
>> - return -EINVAL;
>> - }
>> - ec_device = ec_dev->ec_dev;
>> -
>> indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
>> if (!indio_dev)
>> return -ENOMEM;
>> diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
>> index 48b2a30..dbea18b 100644
>> --- a/drivers/iio/pressure/cros_ec_baro.c
>> +++ b/drivers/iio/pressure/cros_ec_baro.c
>> @@ -126,19 +126,11 @@ static const struct iio_info cros_ec_baro_info = {
>> static int cros_ec_baro_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
>> - struct cros_ec_device *ec_device;
>> struct iio_dev *indio_dev;
>> struct cros_ec_baro_state *state;
>> struct iio_chan_spec *channel;
>> int ret;
>>
>> - if (!ec_dev || !ec_dev->ec_dev) {
>> - dev_warn(dev, "No CROS EC device found.\n");
>> - return -EINVAL;
>> - }
>> - ec_device = ec_dev->ec_dev;
>> -
>> indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
>> if (!indio_dev)
>> return -ENOMEM;
>> diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
>> index b0ca5a4c..75a27a6 100644
>> --- a/drivers/mfd/cros_ec.c
>> +++ b/drivers/mfd/cros_ec.c
>> @@ -91,6 +91,160 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
>> return cros_ec_cmd_xfer(ec_dev, &buf.msg);
>> }
>>
>> +static int cros_ec_check_features(struct cros_ec_device *ec_dev, int feature)
>> +{
>> + struct cros_ec_command *msg;
>> + int ret;
>> +
>> + if (ec_dev->features[0] == -1U && ec_dev->features[1] == -1U) {
>> + /* features bitmap not read yet */
>> +
>> + msg = kmalloc(sizeof(*msg) + sizeof(ec_dev->features),
>> + GFP_KERNEL);
>> + if (!msg)
>> + return -ENOMEM;
>> +
>> + msg->version = 0;
>> + msg->command = EC_CMD_GET_FEATURES + ec_p.cmd_offset;
>> + msg->insize = sizeof(ec_dev->features);
>> + msg->outsize = 0;
>> +
>> + ret = cros_ec_cmd_xfer(ec_dev, msg);
>> + if (ret < 0 || msg->result != EC_RES_SUCCESS) {
>> + dev_warn(ec_dev->dev, "cannot get EC features: %d/%d\n",
>> + ret, msg->result);
>> + memset(ec_dev->features, 0, sizeof(ec_dev->features));
>> + }
>> +
>> + memcpy(ec_dev->features, msg->data, sizeof(ec_dev->features));
>> +
>> + dev_dbg(ec_dev->dev, "EC features %08x %08x\n",
>> + ec_dev->features[0], ec_dev->features[1]);
>> +
>> + kfree(msg);
>> + }
>> +
>> + return ec_dev->features[feature / 32] & EC_FEATURE_MASK_0(feature);
>> +}
>> +
>> +static void cros_ec_sensors_register(struct cros_ec_device *ec_dev)
>> +{
>> + /*
>> + * Issue a command to get the number of sensor reported.
>> + * Build an array of sensors driver and register them all.
>> + */
>> + int ret, i, id, sensor_num;
>> + struct mfd_cell *sensor_cells;
>> + struct cros_ec_sensor_platform *sensor_platforms;
>> + int sensor_type[MOTIONSENSE_TYPE_MAX];
>> + struct ec_params_motion_sense *params;
>> + struct ec_response_motion_sense *resp;
>> + struct cros_ec_command *msg;
>> +
>> + msg = kzalloc(sizeof(struct cros_ec_command) +
>> + max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
>> + if (msg == NULL)
>> + return;
>> +
>> + msg->version = 2;
>> + msg->command = EC_CMD_MOTION_SENSE_CMD + ec_p.cmd_offset;
>> + msg->outsize = sizeof(*params);
>> + msg->insize = sizeof(*resp);
>> +
>> + params = (struct ec_params_motion_sense *)msg->data;
>> + params->cmd = MOTIONSENSE_CMD_DUMP;
>> +
>> + ret = cros_ec_cmd_xfer(ec_dev, msg);
>> + if (ret < 0 || msg->result != EC_RES_SUCCESS) {
>> + dev_warn(ec_dev->dev, "cannot get EC sensor information: %d/%d\n",
>> + ret, msg->result);
>> + goto error;
>> + }
>> +
>> + resp = (struct ec_response_motion_sense *)msg->data;
>> + sensor_num = resp->dump.sensor_count;
>> + /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
>> + sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
>> + GFP_KERNEL);
>> + if (sensor_cells == NULL)
>> + goto error;
>> +
>> + sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
>> + (sensor_num + 1), GFP_KERNEL);
>> + if (sensor_platforms == NULL)
>> + goto error_platforms;
>> +
>> + memset(sensor_type, 0, sizeof(sensor_type));
>> + id = 0;
>> + for (i = 0; i < sensor_num; i++) {
>> + params->cmd = MOTIONSENSE_CMD_INFO;
>> + params->info.sensor_num = i;
>> + ret = cros_ec_cmd_xfer(ec_dev, msg);
>> + if (ret < 0 || msg->result != EC_RES_SUCCESS) {
>> + dev_warn(ec_dev->dev, "no info for EC sensor %d : %d/%d\n",
>> + i, ret, msg->result);
>> + continue;
>> + }
>> + switch (resp->info.type) {
>> + case MOTIONSENSE_TYPE_ACCEL:
>> + sensor_cells[id].name = "cros-ec-accel";
>> + break;
>> + case MOTIONSENSE_TYPE_BARO:
>> + sensor_cells[id].name = "cros-ec-baro";
>> + break;
>> + case MOTIONSENSE_TYPE_GYRO:
>> + sensor_cells[id].name = "cros-ec-gyro";
>> + break;
>> + case MOTIONSENSE_TYPE_MAG:
>> + sensor_cells[id].name = "cros-ec-mag";
>> + break;
>> + case MOTIONSENSE_TYPE_PROX:
>> + sensor_cells[id].name = "cros-ec-prox";
>> + break;
>> + case MOTIONSENSE_TYPE_LIGHT:
>> + sensor_cells[id].name = "cros-ec-light";
>> + break;
>> + case MOTIONSENSE_TYPE_ACTIVITY:
>> + sensor_cells[id].name = "cros-ec-activity";
>> + break;
>> + default:
>> + dev_warn(ec_dev->dev, "unknown type %d\n",
>> + resp->info.type);
>> + continue;
>> + }
>> + sensor_platforms[id].sensor_num = i;
>> + sensor_platforms[id].cmd_offset = ec_p.cmd_offset;
>> + sensor_cells[id].id = sensor_type[resp->info.type];
>> + sensor_cells[id].platform_data = &sensor_platforms[id];
>> + sensor_cells[id].pdata_size =
>> + sizeof(struct cros_ec_sensor_platform);
>> +
>> + sensor_type[resp->info.type]++;
>> + id++;
>> + }
>> + if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
>> + sensor_platforms[id].sensor_num = sensor_num;
>> +
>> + sensor_cells[id].name = "cros-ec-angle";
>> + sensor_cells[id].id = 0;
>> + sensor_cells[id].platform_data = &sensor_platforms[id];
>> + sensor_cells[id].pdata_size =
>> + sizeof(struct cros_ec_sensor_platform);
>> + id++;
>> + }
>> +
>> + ret = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, sensor_cells,
>> + id, NULL, 0, NULL);
>> + if (ret)
>> + dev_err(ec_dev->dev, "failed to add EC sensors\n");
>> +
>> + kfree(sensor_platforms);
>> +error_platforms:
>> + kfree(sensor_cells);
>> +error:
>> + kfree(msg);
>> +}
>> +
>> int cros_ec_register(struct cros_ec_device *ec_dev)
>> {
>> struct device *dev = ec_dev->dev;
>> @@ -101,6 +255,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
>> ec_dev->max_request = sizeof(struct ec_params_hello);
>> ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
>> ec_dev->max_passthru = 0;
>> + ec_dev->features[0] = -1U; /* Not cached yet */
>> + ec_dev->features[1] = -1U; /* Not cached yet */
>>
>> ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
>> if (!ec_dev->din)
>> @@ -134,6 +290,10 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
>> goto fail_mfd;
>> }
>>
>> + /* Check whether this EC is a sensor hub. */
>> + if (cros_ec_check_features(ec_dev, EC_FEATURE_MOTION_SENSE))
>> + cros_ec_sensors_register(ec_dev);
>> +
>> if (ec_dev->max_passthru) {
>> /*
>> * Register a PD device as well on top of this device.
>> diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
>> index cf6c4f0..bd07df5 100644
>> --- a/drivers/platform/chrome/cros_ec_dev.c
>> +++ b/drivers/platform/chrome/cros_ec_dev.c
>> @@ -90,41 +90,6 @@ static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
>> return ret;
>> }
>>
>> -static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
>> -{
>> - struct cros_ec_command *msg;
>> - int ret;
>> -
>> - if (ec->features[0] == -1U && ec->features[1] == -1U) {
>> - /* features bitmap not read yet */
>> -
>> - msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
>> - if (!msg)
>> - return -ENOMEM;
>> -
>> - msg->version = 0;
>> - msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
>> - msg->insize = sizeof(ec->features);
>> - msg->outsize = 0;
>> -
>> - ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
>> - if (ret < 0 || msg->result != EC_RES_SUCCESS) {
>> - dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
>> - ret, msg->result);
>> - memset(ec->features, 0, sizeof(ec->features));
>> - }
>> -
>> - memcpy(ec->features, msg->data, sizeof(ec->features));
>> -
>> - dev_dbg(ec->dev, "EC features %08x %08x\n",
>> - ec->features[0], ec->features[1]);
>> -
>> - kfree(msg);
>> - }
>> -
>> - return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
>> -}
>> -
>> /* Device file ops */
>> static int ec_device_open(struct inode *inode, struct file *filp)
>> {
>> @@ -268,126 +233,6 @@ static void __remove(struct device *dev)
>> kfree(ec);
>> }
>>
>> -static void cros_ec_sensors_register(struct cros_ec_dev *ec)
>> -{
>> - /*
>> - * Issue a command to get the number of sensor reported.
>> - * Build an array of sensors driver and register them all.
>> - */
>> - int ret, i, id, sensor_num;
>> - struct mfd_cell *sensor_cells;
>> - struct cros_ec_sensor_platform *sensor_platforms;
>> - int sensor_type[MOTIONSENSE_TYPE_MAX];
>> - struct ec_params_motion_sense *params;
>> - struct ec_response_motion_sense *resp;
>> - struct cros_ec_command *msg;
>> -
>> - msg = kzalloc(sizeof(struct cros_ec_command) +
>> - max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
>> - if (msg == NULL)
>> - return;
>> -
>> - msg->version = 2;
>> - msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
>> - msg->outsize = sizeof(*params);
>> - msg->insize = sizeof(*resp);
>> -
>> - params = (struct ec_params_motion_sense *)msg->data;
>> - params->cmd = MOTIONSENSE_CMD_DUMP;
>> -
>> - ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
>> - if (ret < 0 || msg->result != EC_RES_SUCCESS) {
>> - dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
>> - ret, msg->result);
>> - goto error;
>> - }
>> -
>> - resp = (struct ec_response_motion_sense *)msg->data;
>> - sensor_num = resp->dump.sensor_count;
>> - /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
>> - sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
>> - GFP_KERNEL);
>> - if (sensor_cells == NULL)
>> - goto error;
>> -
>> - sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
>> - (sensor_num + 1), GFP_KERNEL);
>> - if (sensor_platforms == NULL)
>> - goto error_platforms;
>> -
>> - memset(sensor_type, 0, sizeof(sensor_type));
>> - id = 0;
>> - for (i = 0; i < sensor_num; i++) {
>> - params->cmd = MOTIONSENSE_CMD_INFO;
>> - params->info.sensor_num = i;
>> - ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
>> - if (ret < 0 || msg->result != EC_RES_SUCCESS) {
>> - dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
>> - i, ret, msg->result);
>> - continue;
>> - }
>> - switch (resp->info.type) {
>> - case MOTIONSENSE_TYPE_ACCEL:
>> - sensor_cells[id].name = "cros-ec-accel";
>> - break;
>> - case MOTIONSENSE_TYPE_BARO:
>> - sensor_cells[id].name = "cros-ec-baro";
>> - break;
>> - case MOTIONSENSE_TYPE_GYRO:
>> - sensor_cells[id].name = "cros-ec-gyro";
>> - break;
>> - case MOTIONSENSE_TYPE_MAG:
>> - sensor_cells[id].name = "cros-ec-mag";
>> - break;
>> - case MOTIONSENSE_TYPE_PROX:
>> - sensor_cells[id].name = "cros-ec-prox";
>> - break;
>> - case MOTIONSENSE_TYPE_LIGHT:
>> - sensor_cells[id].name = "cros-ec-light";
>> - break;
>> - case MOTIONSENSE_TYPE_ACTIVITY:
>> - sensor_cells[id].name = "cros-ec-activity";
>> - break;
>> - default:
>> - dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
>> - continue;
>> - }
>> - sensor_platforms[id].sensor_num = i;
>> - sensor_cells[id].id = sensor_type[resp->info.type];
>> - sensor_cells[id].platform_data = &sensor_platforms[id];
>> - sensor_cells[id].pdata_size =
>> - sizeof(struct cros_ec_sensor_platform);
>> -
>> - sensor_type[resp->info.type]++;
>> - id++;
>> - }
>> - if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
>> - sensor_platforms[id].sensor_num = sensor_num;
>> -
>> - sensor_cells[id].name = "cros-ec-angle";
>> - sensor_cells[id].id = 0;
>> - sensor_cells[id].platform_data = &sensor_platforms[id];
>> - sensor_cells[id].pdata_size =
>> - sizeof(struct cros_ec_sensor_platform);
>> - id++;
>> - }
>> - if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
>> - sensor_cells[id].name = "cros-ec-ring";
>> - id++;
>> - }
>> -
>> - ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
>> - NULL, 0, NULL);
>> - if (ret)
>> - dev_err(ec->dev, "failed to add EC sensors\n");
>> -
>> - kfree(sensor_platforms);
>> -error_platforms:
>> - kfree(sensor_cells);
>> -error:
>> - kfree(msg);
>> -}
>> -
>> static int ec_device_probe(struct platform_device *pdev)
>> {
>> int retval = -ENOMEM;
>> @@ -402,8 +247,6 @@ static int ec_device_probe(struct platform_device *pdev)
>> ec->ec_dev = dev_get_drvdata(dev->parent);
>> ec->dev = dev;
>> ec->cmd_offset = ec_platform->cmd_offset;
>> - ec->features[0] = -1U; /* Not cached yet */
>> - ec->features[1] = -1U; /* Not cached yet */
>> device_initialize(&ec->class_dev);
>> cdev_init(&ec->cdev, &fops);
>>
>> @@ -432,10 +275,6 @@ static int ec_device_probe(struct platform_device *pdev)
>> if (cros_ec_debugfs_init(ec))
>> dev_warn(dev, "failed to create debugfs directory\n");
>>
>> - /* check whether this EC is a sensor hub. */
>> - if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
>> - cros_ec_sensors_register(ec);
>> -
>> /* Take control of the lightbar from the EC. */
>> lb_manual_suspend_ctrl(ec, 1);
>>
>> diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
>> index 4e887ba..a4138a5 100644
>> --- a/include/linux/mfd/cros_ec.h
>> +++ b/include/linux/mfd/cros_ec.h
>> @@ -115,6 +115,7 @@ struct cros_ec_command {
>> * @event_notifier: interrupt event notifier for transport devices.
>> * @event_data: raw payload transferred with the MKBP event.
>> * @event_size: size in bytes of the event data.
>> + * @features: stores the EC features.
>> */
>> struct cros_ec_device {
>>
>> @@ -150,15 +151,19 @@ struct cros_ec_device {
>> struct ec_response_get_next_event event_data;
>> int event_size;
>> u32 host_event_wake_mask;
>> + u32 features[2];
>> };
>>
>> /**
>> * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information
>> *
>> * @sensor_num: Id of the sensor, as reported by the EC.
>> + * @cmd_offset: offset to apply for each command. Set when
>> + * registering a devicde behind another one.
>> */
>> struct cros_ec_sensor_platform {
>> u8 sensor_num;
>> + u16 cmd_offset;
>> };
>>
>> /* struct cros_ec_platform - ChromeOS EC platform information
>> @@ -192,7 +197,6 @@ struct cros_ec_dev {
>> struct device *dev;
>> struct cros_ec_debugfs *debug_info;
>> u16 cmd_offset;
>> - u32 features[2];
>> };
>>
>> /**
>> --
>> 2.9.3
>>
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device
From: Lee Jones @ 2017-07-17 7:51 UTC (permalink / raw)
To: Moritz Fischer
Cc: robh+dt, mark.rutland, a.zummo, alexandre.belloni, wim, linux,
devicetree, linux-kernel, linux-watchdog, linux-rtc,
Moritz Fischer
In-Reply-To: <1499975665-14581-2-git-send-email-mdf@kernel.org>
On Thu, 13 Jul 2017, Moritz Fischer wrote:
> From: Moritz Fischer <moritz.fischer@ettus.com>
>
> Add support for the Maxim/Dallas DS1374 RTC/WDT with trickle charger.
> The device can either be configured as simple RTC, as simple RTC with
> Alarm (IRQ) as well as simple RTC with watchdog timer.
>
> Break up the old monolithic driver in drivers/rtc/rtc-ds1374.c into:
> - rtc part in drivers/rtc/rtc-ds1374.c
> - watchdog part under drivers/watchdog/ds1374-wdt.c
> - mfd part drivers/mfd/ds1374.c
>
> The MFD part takes care of trickle charging and mode selection,
> since the usage modes of a) RTC + Alarm or b) RTC + WDT
> are mutually exclusive.
>
> Signed-off-by: Moritz Fischer <mdf@kernel.org>
> ---
> drivers/mfd/Kconfig | 10 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/ds1374.c | 260 ++++++++++++++++
> drivers/rtc/rtc-ds1374.c | 639 ++++++++++-----------------------------
It looks like this should now depend on MFD_DS1374, right?
> drivers/watchdog/Kconfig | 10 +
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/ds1374-wdt.c | 208 +++++++++++++
The RTC and Watchdog drivers need to be split out of this patch and
placed into their own. Then we can take them through their respective
subsystem trees and do not have to rely on immutable branches for
unification.
> include/dt-bindings/mfd/ds1374.h | 17 ++
> include/linux/mfd/ds1374.h | 59 ++++
> 9 files changed, 722 insertions(+), 483 deletions(-)
> create mode 100644 drivers/mfd/ds1374.c
> create mode 100644 drivers/watchdog/ds1374-wdt.c
> create mode 100644 include/dt-bindings/mfd/ds1374.h
> create mode 100644 include/linux/mfd/ds1374.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3eb5c93..2dfef3c 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -203,6 +203,16 @@ config MFD_CROS_EC_SPI
> response time cannot be guaranteed, we support ignoring
> 'pre-amble' bytes before the response actually starts.
>
> +config MFD_DS1374
> + tristate "Dallas/Maxim DS1374 RTC/WDT/ALARM (I2C)"
> + select MFD_CORE
> + depends on I2C
> + depends on REGMAP_I2C
> +
> + ---help---
This is an old style of help. Please remove the '-'s.
> + This driver supports the Dallas Maxim DS1374 multi function chip.
"Multi-Functional"
> + The chip combines an RTC, trickle charger, Watchdog or Alarm.
Why is "trickle charger" not capitalised?
> +
> config MFD_ASIC3
> bool "Compaq ASIC3"
> depends on GPIOLIB && ARM
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index c16bf1e..b5cfcf4 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -15,6 +15,7 @@ cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o
> 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_DS1374) += ds1374.o
> obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
>
> rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
> diff --git a/drivers/mfd/ds1374.c b/drivers/mfd/ds1374.c
> new file mode 100644
> index 0000000..a0cfa1b
> --- /dev/null
> +++ b/drivers/mfd/ds1374.c
> @@ -0,0 +1,260 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Dallas/Maxim DS1374 Multi Function Device Driver
"Functional"
> + * The trickle charger code was taken more ore less 1:1 from
"or"
> + * drivers/rtc/rtc-1390.c
Why does this need to be documented in this file?
If the Trickle Charger code is in here (I haven't been down that far
yet) you need to move it out into a more appropriate subsystem.
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/pm.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/ds1374.h>
Alphabetical.
> +#define DS1374_TRICKLE_CHARGER_ENABLE 0xa0
> +#define DS1374_TRICKLE_CHARGER_ENABLE_MASK 0xe0
> +
> +#define DS1374_TRICKLE_CHARGER_250_OHM 0x01
> +#define DS1374_TRICKLE_CHARGER_2K_OHM 0x02
> +#define DS1374_TRICKLE_CHARGER_4K_OHM 0x03
> +#define DS1374_TRICKLE_CHARGER_ROUT_MASK 0x03
> +
> +#define DS1374_TRICKLE_CHARGER_NO_DIODE 0x04
> +#define DS1374_TRICKLE_CHARGER_DIODE 0x08
> +#define DS1374_TRICKLE_CHARGER_DIODE_MASK 0xc
Are these tabs or spaces, or a mixture?
Did you run checkpatch.pl?
> +static const struct regmap_range volatile_ranges[] = {
> + regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_WDALM2),
> + regmap_reg_range(DS1374_REG_SR, DS1374_REG_SR),
> +};
> +
> +static const struct regmap_access_table ds1374_volatile_table = {
> + .yes_ranges = volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
> +};
> +
> +static struct regmap_config ds1374_regmap_config = {
Genuine question: Can this be const?
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = DS1374_REG_TCR,
> + .volatile_table = &ds1374_volatile_table,
> + .cache_type = REGCACHE_RBTREE,
It might just be the patch format, but can you check if this is
tabs/spaces? If you're using tabs, please ensure they are all
aligned.
> +};
> +
> +static struct mfd_cell ds1374_wdt_cell = {
> + .name = "ds1374-wdt",
> +};
> +
> +static struct mfd_cell ds1374_rtc_cell = {
> + .name = "ds1374-rtc",
> +};
> +
> +static int ds1374_add_device(struct ds1374 *chip,
> + struct mfd_cell *cell)
> +{
> + cell->platform_data = chip;
> + cell->pdata_size = sizeof(*chip);
> +
> + return mfd_add_devices(&chip->client->dev, PLATFORM_DEVID_AUTO,
> + cell, 1, NULL, 0, NULL);
> +}
This function appears to serve no purpose. Why don't you just use
mfd_add_devices() instead?
> +static int ds1374_trickle_of_init(struct ds1374 *ds1374)
> +{
> + u32 ohms = 0;
> + u8 value;
> + struct i2c_client *client = ds1374->client;
> +
> + if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms",
> + &ohms))
> + return 0;
> +
> + /* Enable charger */
> + value = DS1374_TRICKLE_CHARGER_ENABLE;
> + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
> + value |= DS1374_TRICKLE_CHARGER_NO_DIODE;
> + else
> + value |= DS1374_TRICKLE_CHARGER_DIODE;
> +
> + /* Resistor select */
> + switch (ohms) {
> + case 250:
> + value |= DS1374_TRICKLE_CHARGER_250_OHM;
> + break;
> + case 2000:
> + value |= DS1374_TRICKLE_CHARGER_2K_OHM;
> + break;
> + case 4000:
> + value |= DS1374_TRICKLE_CHARGER_4K_OHM;
> + break;
> + default:
> + dev_warn(&client->dev,
> + "Unsupported ohm value %02ux in dt\n", ohms);
> + return -EINVAL;
> + }
> + dev_dbg(&client->dev, "Trickle charge value is 0x%02x\n", value);
> +
> + return regmap_write(ds1374->regmap, DS1374_REG_TCR, value);
> +}
> +
> +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes)
> +{
> + u8 buf[4];
> + int ret;
> + int i;
> +
> + if (WARN_ON(nbytes > 4))
> + return -EINVAL;
> +
> + ret = regmap_bulk_read(ds1374->regmap, reg, buf, nbytes);
> + if (ret) {
> + dev_err(&ds1374->client->dev,
> + "Failed to bulkread n = %d at R%d\n",
> + nbytes, reg);
> + return ret;
> + }
> +
> + for (i = nbytes - 1, *time = 0; i >= 0; i--)
> + *time = (*time << 8) | buf[i];
You need at least a comment to explain what's happening here.
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(ds1374_read_bulk);
> +
> +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes)
> +{
> + u8 buf[4];
> + int i;
> +
> + if (nbytes > 4) {
> + WARN_ON(1);
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < nbytes; i++) {
> + buf[i] = time & 0xff;
> + time >>= 8;
> + }
Same here.
> + return regmap_bulk_write(ds1374->regmap, reg, buf, nbytes);
> +}
> +EXPORT_SYMBOL_GPL(ds1374_write_bulk);
> +
> +static int ds1374_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct ds1374 *ds1374;
> + u32 mode;
> + int err;
> +
> + ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
> + if (!ds1374)
> + return -ENOMEM;
> +
> + ds1374->regmap = devm_regmap_init_i2c(client, &ds1374_regmap_config);
> + if (IS_ERR(ds1374->regmap))
> + return PTR_ERR(ds1374->regmap);
> +
> + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
> + err = of_property_read_u32(client->dev.of_node,
> + "dallas,mode", &mode);
> + if (err < 0) {
> + dev_err(&client->dev, "missing dallas,mode property\n");
> + return -EINVAL;
> + }
> +
> + ds1374->remapped_reset
> + = of_property_read_bool(client->dev.of_node,
> + "dallas,remap-reset");
> +
> + ds1374->mode = (enum ds1374_mode)mode;
> + } else if (IS_ENABLED(CONFIG_RTC_DRV_DS1374_WDT)) {
> + ds1374->mode = DS1374_MODE_RTC_WDT;
> + } else {
> + ds1374->mode = DS1374_MODE_RTC_ALM;
> + }
This is non-standard. So if OF is enabled, you're taking the 'mode'
from platform data (DT) and if it's not, you're relying on Kconfig.
May I suggest that you pick platform data OR Kconfig, but not mix the
two.
> + ds1374->client = client;
> + ds1374->irq = client->irq;
> + i2c_set_clientdata(client, ds1374);
> +
> + /* check if we're supposed to trickle charge */
"Check".
> + err = ds1374_trickle_of_init(ds1374);
> + if (err) {
> + dev_err(&client->dev, "Failed to init trickle charger!\n");
> + return err;
> + }
> +
> + /* we always have a rtc */
"We", "an RTC".
Or
"The RTC is always available."
> + err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
> + if (err)
> + return err;
> +
> + /* we might have a watchdog if configured that way */
"We"
> + if (ds1374->mode == DS1374_MODE_RTC_WDT)
> + return ds1374_add_device(ds1374, &ds1374_wdt_cell);
> +
> + return err;
> +}
> +
> +static const struct i2c_device_id ds1374_id[] = {
> + { "ds1374", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, ds1374_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id ds1374_of_match[] = {
> + { .compatible = "dallas,ds1374" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, ds1374_of_match);
> +#endif
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ds1374_suspend(struct device *dev)
> +{
> + return 0;
> +}
> +
> +static int ds1374_resume(struct device *dev)
> +{
> + return 0;
> +}
> +#endif
These seem pointless.
I'm sure there will be a nice MACRO you can use instead.
> +static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
> +
> +static struct i2c_driver ds1374_driver = {
> + .driver = {
> + .name = "ds1374",
> + .of_match_table = of_match_ptr(ds1374_of_match),
> + .pm = &ds1374_pm,
> + },
> + .probe = ds1374_probe,
> + .id_table = ds1374_id,
> +};
> +
> +static int __init ds1374_init(void)
> +{
> + return i2c_add_driver(&ds1374_driver);
> +}
> +subsys_initcall(ds1374_init);
> +
> +static void __exit ds1374_exit(void)
> +{
> + i2c_del_driver(&ds1374_driver);
> +}
> +module_exit(ds1374_exit);
> +
> +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> +MODULE_DESCRIPTION("Maxim/Dallas DS1374 MFD Driver");
> +MODULE_LICENSE("GPL");
This conflicts with your header.
[...]
> diff --git a/include/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
> new file mode 100644
> index 0000000..b33cd5e
> --- /dev/null
> +++ b/include/dt-bindings/mfd/ds1374.h
> @@ -0,0 +1,17 @@
> +/*
> + * This header provides macros for Maxim/Dallas DS1374 DT bindings
> + *
> + * Copyright (C) 2017 National Instruments Corp
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + *
This line is superfluous.
> + */
> +
> +#ifndef __DT_BINDINGS_MFD_DS1374_H__
> +#define __DT_BINDINGS_MFD_DS1374_H__
> +
> +#define DALLAS_MODE_RTC 0
> +#define DALLAS_MODE_ALM 1
> +#define DALLAS_MODE_WDT 2
What's stopping these becoming out of line with the #defines in the
other header file? You should probably use these for comparison
instead.
> +#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
> diff --git a/include/linux/mfd/ds1374.h b/include/linux/mfd/ds1374.h
> new file mode 100644
> index 0000000..7b697f8
> --- /dev/null
> +++ b/include/linux/mfd/ds1374.h
> @@ -0,0 +1,59 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Multi Function Device for Dallas/Maxim DS1374 RTC/WDT
"Functional"
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
Do you have to use the long licence here?
> + */
> +
> +#ifndef MFD_DS1374_H
> +#define MFD_DS1374_H
> +
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +
> +enum ds1374_mode {
> + DS1374_MODE_RTC_ONLY,
> + DS1374_MODE_RTC_ALM,
> + DS1374_MODE_RTC_WDT,
> +};
Please see above.
> +/* Register definitions to for all subdrivers
> + */
> +#define DS1374_REG_TOD0 0x00 /* Time of Day */
> +#define DS1374_REG_TOD1 0x01
> +#define DS1374_REG_TOD2 0x02
> +#define DS1374_REG_TOD3 0x03
> +#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
> +#define DS1374_REG_WDALM1 0x05
> +#define DS1374_REG_WDALM2 0x06
> +#define DS1374_REG_CR 0x07 /* Control */
> +#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
> +#define DS1374_REG_CR_WDSTR 0x08 /* 1=Reset on INT, 0=Rreset on RST */
> +#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
> +#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
> +#define DS1374_REG_SR 0x08 /* Status */
> +#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
> +#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
> +#define DS1374_REG_TCR 0x09 /* Trickle Charge */
> +
> +struct ds1374 {
> + struct i2c_client *client;
> + struct regmap *regmap;
> + int irq;
> + enum ds1374_mode mode;
> + bool remapped_reset;
> +};
This could do with a KernelDoc header.
> +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes);
> +
> +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes);
Do these two just read time?
If so, the nomenclature could be improved.
> +#endif /* MFD_DS1374_H */
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [rtc-linux] Re: [PATCH] RTC: Add functionality to read/write rtc scratch registers
From: Keerthy @ 2017-07-16 13:49 UTC (permalink / raw)
To: Alexandre Belloni; +Cc: linux-rtc, linux-kernel, linux-omap, Russ Dill
In-Reply-To: <20170713204355.sbtjip5skms6zwdq@piout.net>
On Friday 14 July 2017 02:13 AM, Alexandre Belloni wrote:
> Hi,
>
> On 05/05/2017 at 00:38:27 +0200, Alexandre Belloni wrote:
>> Hi,
>>
>> On 03/05/2017 at 11:39:34 +0530, Keerthy wrote:
>>> On Tuesday 18 April 2017 10:50 AM, Keerthy wrote:
>>>> From: Russ Dill <Russ.Dill@ti.com>
>>>>
>>>> Many RTCs provide scratch registers that are maintained so long as the RTC
>>>> has power. Provide a generic method to access these registers.
>>>>
>>>
>>> A gentle ping on this
>>>
>>
>> Yeah, I apologize for the delay (I know you already sent this 6-8 months
>> ago).
>> I'm not satisfied with the interface (and its name). I gave a lot of
>> thought into it and I'm currently reworking part of the subsystem to
>> create a similar feature. I'm planning to have it in 4.13.
>>
>
> So, the nvmem stuff is now in 4.13. If you can still wait a bit, I'll
> implement it myself and test on am335x.
Sure.
>
>
^ permalink raw reply
* Re: [PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device
From: Guenter Roeck @ 2017-07-14 19:27 UTC (permalink / raw)
To: Moritz Fischer
Cc: lee.jones, robh+dt, mark.rutland, a.zummo, alexandre.belloni, wim,
devicetree, linux-kernel, linux-watchdog, linux-rtc,
Moritz Fischer
In-Reply-To: <20170714165423.GA9283@tyrael.ni.corp.natinst.com>
On Fri, Jul 14, 2017 at 09:54:23AM -0700, Moritz Fischer wrote:
> Hi Guenter,
>
> On Thu, Jul 13, 2017 at 08:57:52PM -0700, Guenter Roeck wrote:
> > On 07/13/2017 12:54 PM, Moritz Fischer wrote:
> > > From: Moritz Fischer <moritz.fischer@ettus.com>
> > >
> > > Add support for the Maxim/Dallas DS1374 RTC/WDT with trickle charger.
> > > The device can either be configured as simple RTC, as simple RTC with
> > > Alarm (IRQ) as well as simple RTC with watchdog timer.
> > >
> > > Break up the old monolithic driver in drivers/rtc/rtc-ds1374.c into:
> > > - rtc part in drivers/rtc/rtc-ds1374.c
> > > - watchdog part under drivers/watchdog/ds1374-wdt.c
> > > - mfd part drivers/mfd/ds1374.c
> > >
> > > The MFD part takes care of trickle charging and mode selection,
> > > since the usage modes of a) RTC + Alarm or b) RTC + WDT
> > > are mutually exclusive.
> > >
> > > Signed-off-by: Moritz Fischer <mdf@kernel.org>
> >
> > [ Only reviewing watchdog part ]
> >
[ ... ]
> > > +}
> > > +
> > > +static int ds1374_wdt_start(struct watchdog_device *wdog)
> > > +{
> > > + int err;
> > > + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> > > +
> > > + err = ds1374_wdt_set_timeout(wdog, wdog->timeout);
> > > + if (err) {
> > > + dev_err(ds1374_wdt->dev, "%s: failed to set timeout (%d) %u\n",
> > > + __func__, err, wdog->timeout);
> > > + return err;
> > > + }
> > > +
> > > + err = ds1374_wdt_ping(wdog);
> > > + if (err) {
> > > + dev_err(ds1374_wdt->dev, "%s: failed to ping (%d)\n", __func__,
> > > + err);
>
> I assume you'd want to get rid of that one too?
Yes.
> >
> > This may bypass MAGICCLOSE: If the watchdog daemon is killed and the module removed,
> > the watchdog will be stopped. Is this really what you want ? If so, why set MAGICCLOSE
> > in the first place ?
>
> So your suggestion would be:
>
> - if (!nowayout)
> - ds1374_wdt_stop(&priv->wdd)
>
Correct. Just drop the remove function.
Thanks,
Guenter
^ permalink raw reply
* Re: [PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device
From: Moritz Fischer @ 2017-07-14 16:54 UTC (permalink / raw)
To: Guenter Roeck
Cc: Moritz Fischer, lee.jones, robh+dt, mark.rutland, a.zummo,
alexandre.belloni, wim, devicetree, linux-kernel, linux-watchdog,
linux-rtc, Moritz Fischer
In-Reply-To: <c37eaed9-10aa-190e-fa0e-bdce9277664b@roeck-us.net>
[-- Attachment #1: Type: text/plain, Size: 51387 bytes --]
Hi Guenter,
On Thu, Jul 13, 2017 at 08:57:52PM -0700, Guenter Roeck wrote:
> On 07/13/2017 12:54 PM, Moritz Fischer wrote:
> > From: Moritz Fischer <moritz.fischer@ettus.com>
> >
> > Add support for the Maxim/Dallas DS1374 RTC/WDT with trickle charger.
> > The device can either be configured as simple RTC, as simple RTC with
> > Alarm (IRQ) as well as simple RTC with watchdog timer.
> >
> > Break up the old monolithic driver in drivers/rtc/rtc-ds1374.c into:
> > - rtc part in drivers/rtc/rtc-ds1374.c
> > - watchdog part under drivers/watchdog/ds1374-wdt.c
> > - mfd part drivers/mfd/ds1374.c
> >
> > The MFD part takes care of trickle charging and mode selection,
> > since the usage modes of a) RTC + Alarm or b) RTC + WDT
> > are mutually exclusive.
> >
> > Signed-off-by: Moritz Fischer <mdf@kernel.org>
>
> [ Only reviewing watchdog part ]
>
> > ---
> > drivers/mfd/Kconfig | 10 +
> > drivers/mfd/Makefile | 1 +
> > drivers/mfd/ds1374.c | 260 ++++++++++++++++
> > drivers/rtc/rtc-ds1374.c | 639 ++++++++++-----------------------------
> > drivers/watchdog/Kconfig | 10 +
> > drivers/watchdog/Makefile | 1 +
> > drivers/watchdog/ds1374-wdt.c | 208 +++++++++++++
> > include/dt-bindings/mfd/ds1374.h | 17 ++
> > include/linux/mfd/ds1374.h | 59 ++++
> > 9 files changed, 722 insertions(+), 483 deletions(-)
> > create mode 100644 drivers/mfd/ds1374.c
> > create mode 100644 drivers/watchdog/ds1374-wdt.c
> > create mode 100644 include/dt-bindings/mfd/ds1374.h
> > create mode 100644 include/linux/mfd/ds1374.h
> >
>
> If possible, it might be better to split the patch into multiple parts, one per subsystem.
I'll have to think about that some more ;-)
>
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 3eb5c93..2dfef3c 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -203,6 +203,16 @@ config MFD_CROS_EC_SPI
> > response time cannot be guaranteed, we support ignoring
> > 'pre-amble' bytes before the response actually starts.
> > +config MFD_DS1374
> > + tristate "Dallas/Maxim DS1374 RTC/WDT/ALARM (I2C)"
> > + select MFD_CORE
> > + depends on I2C
> > + depends on REGMAP_I2C
> > +
> > + ---help---
> > + This driver supports the Dallas Maxim DS1374 multi function chip.
> > + The chip combines an RTC, trickle charger, Watchdog or Alarm.
> > +
> > config MFD_ASIC3
> > bool "Compaq ASIC3"
> > depends on GPIOLIB && ARM
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index c16bf1e..b5cfcf4 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -15,6 +15,7 @@ cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o
> > 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_DS1374) += ds1374.o
> > obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
> > rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
> > diff --git a/drivers/mfd/ds1374.c b/drivers/mfd/ds1374.c
> > new file mode 100644
> > index 0000000..a0cfa1b
> > --- /dev/null
> > +++ b/drivers/mfd/ds1374.c
> > @@ -0,0 +1,260 @@
> > +/*
> > + * Copyright (c) 2017, National Instruments Corp.
> > + *
> > + * Dallas/Maxim DS1374 Multi Function Device Driver
> > + *
> > + * The trickle charger code was taken more ore less 1:1 from
> > + * drivers/rtc/rtc-1390.c
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/i2c.h>
> > +#include <linux/slab.h>
> > +#include <linux/pm.h>
> > +#include <linux/regmap.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/mfd/ds1374.h>
> > +
> > +#define DS1374_TRICKLE_CHARGER_ENABLE 0xa0
> > +#define DS1374_TRICKLE_CHARGER_ENABLE_MASK 0xe0
> > +
> > +#define DS1374_TRICKLE_CHARGER_250_OHM 0x01
> > +#define DS1374_TRICKLE_CHARGER_2K_OHM 0x02
> > +#define DS1374_TRICKLE_CHARGER_4K_OHM 0x03
> > +#define DS1374_TRICKLE_CHARGER_ROUT_MASK 0x03
> > +
> > +#define DS1374_TRICKLE_CHARGER_NO_DIODE 0x04
> > +#define DS1374_TRICKLE_CHARGER_DIODE 0x08
> > +#define DS1374_TRICKLE_CHARGER_DIODE_MASK 0xc
> > +
> > +static const struct regmap_range volatile_ranges[] = {
> > + regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_WDALM2),
> > + regmap_reg_range(DS1374_REG_SR, DS1374_REG_SR),
> > +};
> > +
> > +static const struct regmap_access_table ds1374_volatile_table = {
> > + .yes_ranges = volatile_ranges,
> > + .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
> > +};
> > +
> > +static struct regmap_config ds1374_regmap_config = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = DS1374_REG_TCR,
> > + .volatile_table = &ds1374_volatile_table,
> > + .cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +static struct mfd_cell ds1374_wdt_cell = {
> > + .name = "ds1374-wdt",
> > +};
> > +
> > +static struct mfd_cell ds1374_rtc_cell = {
> > + .name = "ds1374-rtc",
> > +};
> > +
> > +static int ds1374_add_device(struct ds1374 *chip,
> > + struct mfd_cell *cell)
> > +{
> > + cell->platform_data = chip;
> > + cell->pdata_size = sizeof(*chip);
> > +
> > + return mfd_add_devices(&chip->client->dev, PLATFORM_DEVID_AUTO,
> > + cell, 1, NULL, 0, NULL);
> > +}
> > +
> > +static int ds1374_trickle_of_init(struct ds1374 *ds1374)
> > +{
> > + u32 ohms = 0;
> > + u8 value;
> > + struct i2c_client *client = ds1374->client;
> > +
> > + if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms",
> > + &ohms))
> > + return 0;
> > +
> > + /* Enable charger */
> > + value = DS1374_TRICKLE_CHARGER_ENABLE;
> > + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
> > + value |= DS1374_TRICKLE_CHARGER_NO_DIODE;
> > + else
> > + value |= DS1374_TRICKLE_CHARGER_DIODE;
> > +
> > + /* Resistor select */
> > + switch (ohms) {
> > + case 250:
> > + value |= DS1374_TRICKLE_CHARGER_250_OHM;
> > + break;
> > + case 2000:
> > + value |= DS1374_TRICKLE_CHARGER_2K_OHM;
> > + break;
> > + case 4000:
> > + value |= DS1374_TRICKLE_CHARGER_4K_OHM;
> > + break;
> > + default:
> > + dev_warn(&client->dev,
> > + "Unsupported ohm value %02ux in dt\n", ohms);
> > + return -EINVAL;
> > + }
> > + dev_dbg(&client->dev, "Trickle charge value is 0x%02x\n", value);
> > +
> > + return regmap_write(ds1374->regmap, DS1374_REG_TCR, value);
> > +}
> > +
> > +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes)
> > +{
> > + u8 buf[4];
> > + int ret;
> > + int i;
> > +
> > + if (WARN_ON(nbytes > 4))
> > + return -EINVAL;
> > +
> > + ret = regmap_bulk_read(ds1374->regmap, reg, buf, nbytes);
> > + if (ret) {
> > + dev_err(&ds1374->client->dev,
> > + "Failed to bulkread n = %d at R%d\n",
> > + nbytes, reg);
> > + return ret;
> > + }
> > +
> > + for (i = nbytes - 1, *time = 0; i >= 0; i--)
> > + *time = (*time << 8) | buf[i];
> > +
>
> I think those functions should go away; the calling code can use standard
> endianness conversion functions and call regmap_bulk_{read,write} directly.
Sounds good. I just ported over the code that had been there, but
cleaning up is always a good idea :)
>
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(ds1374_read_bulk);
> > +
> > +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes)
> > +{
> > + u8 buf[4];
> > + int i;
> > +
> > + if (nbytes > 4) {
> > + WARN_ON(1);
> > + return -EINVAL;
> > + }
> > +
> > + for (i = 0; i < nbytes; i++) {
> > + buf[i] = time & 0xff;
> > + time >>= 8;
> > + }
> > +
> > + return regmap_bulk_write(ds1374->regmap, reg, buf, nbytes);
> > +}
> > +EXPORT_SYMBOL_GPL(ds1374_write_bulk);
> > +
> > +static int ds1374_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
> > +{
> > + struct ds1374 *ds1374;
> > + u32 mode;
> > + int err;
> > +
> > + ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
> > + if (!ds1374)
> > + return -ENOMEM;
> > +
> > + ds1374->regmap = devm_regmap_init_i2c(client, &ds1374_regmap_config);
> > + if (IS_ERR(ds1374->regmap))
> > + return PTR_ERR(ds1374->regmap);
> > +
> > + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
> > + err = of_property_read_u32(client->dev.of_node,
> > + "dallas,mode", &mode);
> > + if (err < 0) {
> > + dev_err(&client->dev, "missing dallas,mode property\n");
> > + return -EINVAL;
> > + }
> > +
> > + ds1374->remapped_reset
> > + = of_property_read_bool(client->dev.of_node,
> > + "dallas,remap-reset");
> > +
> > + ds1374->mode = (enum ds1374_mode)mode;
> > + } else if (IS_ENABLED(CONFIG_RTC_DRV_DS1374_WDT)) {
> > + ds1374->mode = DS1374_MODE_RTC_WDT;
> > + } else {
> > + ds1374->mode = DS1374_MODE_RTC_ALM;
> > + }
> > +
> > + ds1374->client = client;
> > + ds1374->irq = client->irq;
> > + i2c_set_clientdata(client, ds1374);
> > +
> > + /* check if we're supposed to trickle charge */
> > + err = ds1374_trickle_of_init(ds1374);
> > + if (err) {
> > + dev_err(&client->dev, "Failed to init trickle charger!\n");
> > + return err;
> > + }
> > +
> > + /* we always have a rtc */
> > + err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
> > + if (err)
> > + return err;
> > +
> > + /* we might have a watchdog if configured that way */
> > + if (ds1374->mode == DS1374_MODE_RTC_WDT)
> > + return ds1374_add_device(ds1374, &ds1374_wdt_cell);
> > +
> > + return err;
> > +}
> > +
> > +static const struct i2c_device_id ds1374_id[] = {
> > + { "ds1374", 0 },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, ds1374_id);
> > +
> > +#ifdef CONFIG_OF
> > +static const struct of_device_id ds1374_of_match[] = {
> > + { .compatible = "dallas,ds1374" },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(of, ds1374_of_match);
> > +#endif
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int ds1374_suspend(struct device *dev)
> > +{
> > + return 0;
> > +}
> > +
> > +static int ds1374_resume(struct device *dev)
> > +{
> > + return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
> > +
> > +static struct i2c_driver ds1374_driver = {
> > + .driver = {
> > + .name = "ds1374",
> > + .of_match_table = of_match_ptr(ds1374_of_match),
> > + .pm = &ds1374_pm,
> > + },
> > + .probe = ds1374_probe,
> > + .id_table = ds1374_id,
> > +};
> > +
> > +static int __init ds1374_init(void)
> > +{
> > + return i2c_add_driver(&ds1374_driver);
> > +}
> > +subsys_initcall(ds1374_init);
> > +
> > +static void __exit ds1374_exit(void)
> > +{
> > + i2c_del_driver(&ds1374_driver);
> > +}
> > +module_exit(ds1374_exit);
> > +
> > +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> > +MODULE_DESCRIPTION("Maxim/Dallas DS1374 MFD Driver");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
> > index 52429f0..29ce1a9 100644
> > --- a/drivers/rtc/rtc-ds1374.c
> > +++ b/drivers/rtc/rtc-ds1374.c
> > @@ -1,75 +1,38 @@
> > /*
> > - * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
> > + * RTC driver for the Maxim/Dallas DS1374 Real-Time Clock via MFD
> > *
> > * Based on code by Randy Vinson <rvinson@mvista.com>,
> > * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
> > *
> > + * Copyright (C) 2017 National Instruments Corp
> > * Copyright (C) 2014 Rose Technology
> > * Copyright (C) 2006-2007 Freescale Semiconductor
> > *
> > + * SPDX-License-Identifier: GPL-2.0
> > + *
> > * 2005 (c) MontaVista Software, Inc. This file is licensed under
> > * the terms of the GNU General Public License version 2. This program
> > * is licensed "as is" without any warranty of any kind, whether express
> > * or implied.
> > */
> > -/*
> > - * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
> > - * recommened in .../Documentation/i2c/writing-clients section
> > - * "Sending and receiving", using SMBus level communication is preferred.
> > - */
> > #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > #include <linux/kernel.h>
> > #include <linux/module.h>
> > #include <linux/interrupt.h>
> > -#include <linux/i2c.h>
> > #include <linux/rtc.h>
> > #include <linux/bcd.h>
> > #include <linux/workqueue.h>
> > #include <linux/slab.h>
> > #include <linux/pm.h>
> > -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> > -#include <linux/fs.h>
> > -#include <linux/ioctl.h>
> > -#include <linux/miscdevice.h>
> > -#include <linux/reboot.h>
> > -#include <linux/watchdog.h>
> > -#endif
> > -
> > -#define DS1374_REG_TOD0 0x00 /* Time of Day */
> > -#define DS1374_REG_TOD1 0x01
> > -#define DS1374_REG_TOD2 0x02
> > -#define DS1374_REG_TOD3 0x03
> > -#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
> > -#define DS1374_REG_WDALM1 0x05
> > -#define DS1374_REG_WDALM2 0x06
> > -#define DS1374_REG_CR 0x07 /* Control */
> > -#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
> > -#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
> > -#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
> > -#define DS1374_REG_SR 0x08 /* Status */
> > -#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
> > -#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
> > -#define DS1374_REG_TCR 0x09 /* Trickle Charge */
> > -
> > -static const struct i2c_device_id ds1374_id[] = {
> > - { "ds1374", 0 },
> > - { }
> > -};
> > -MODULE_DEVICE_TABLE(i2c, ds1374_id);
> > +#include <linux/regmap.h>
> > +#include <linux/mfd/ds1374.h>
> > +#include <linux/platform_device.h>
> > -#ifdef CONFIG_OF
> > -static const struct of_device_id ds1374_of_match[] = {
> > - { .compatible = "dallas,ds1374" },
> > - { }
> > -};
> > -MODULE_DEVICE_TABLE(of, ds1374_of_match);
> > -#endif
> > -
> > -struct ds1374 {
> > - struct i2c_client *client;
> > +struct ds1374_rtc {
> > struct rtc_device *rtc;
> > + struct ds1374 *chip;
> > struct work_struct work;
> > /* The mutex protects alarm operations, and prevents a race
> > @@ -80,89 +43,44 @@ struct ds1374 {
> > int exiting;
> > };
> > -static struct i2c_driver ds1374_driver;
> > -
> > -static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
> > - int reg, int nbytes)
> > -{
> > - u8 buf[4];
> > - int ret;
> > - int i;
> > -
> > - if (WARN_ON(nbytes > 4))
> > - return -EINVAL;
> > -
> > - ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
> > -
> > - if (ret < 0)
> > - return ret;
> > - if (ret < nbytes)
> > - return -EIO;
> > -
> > - for (i = nbytes - 1, *time = 0; i >= 0; i--)
> > - *time = (*time << 8) | buf[i];
> > -
> > - return 0;
> > -}
> > -
> > -static int ds1374_write_rtc(struct i2c_client *client, u32 time,
> > - int reg, int nbytes)
> > -{
> > - u8 buf[4];
> > - int i;
> > -
> > - if (nbytes > 4) {
> > - WARN_ON(1);
> > - return -EINVAL;
> > - }
> > -
> > - for (i = 0; i < nbytes; i++) {
> > - buf[i] = time & 0xff;
> > - time >>= 8;
> > - }
> > -
> > - return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
> > -}
> > -
> > -static int ds1374_check_rtc_status(struct i2c_client *client)
> > +static int ds1374_check_rtc_status(struct ds1374_rtc *ds1374)
> > {
> > int ret = 0;
> > - int control, stat;
> > + unsigned int control, stat;
> > - stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> > - if (stat < 0)
> > + ret = regmap_read(ds1374->chip->regmap, DS1374_REG_SR, &stat);
> > + if (ret)
> > return stat;
> > if (stat & DS1374_REG_SR_OSF)
> > - dev_warn(&client->dev,
> > + dev_warn(&ds1374->chip->client->dev,
> > "oscillator discontinuity flagged, time unreliable\n");
> > - stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
> > -
> > - ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
> > - if (ret < 0)
> > + ret = regmap_update_bits(ds1374->chip->regmap, DS1374_REG_SR,
> > + DS1374_REG_SR_OSF | DS1374_REG_SR_AF, 0);
> > + if (ret)
> > return ret;
> > /* If the alarm is pending, clear it before requesting
> > * the interrupt, so an interrupt event isn't reported
> > * before everything is initialized.
> > */
> > -
> > - control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> > - if (control < 0)
> > - return control;
> > + ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &control);
> > + if (ret)
> > + return ret;
> > control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
> > - return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
> > + return regmap_write(ds1374->chip->regmap, DS1374_REG_CR, control);
> > }
> > static int ds1374_read_time(struct device *dev, struct rtc_time *time)
> > {
> > - struct i2c_client *client = to_i2c_client(dev);
> > + struct platform_device *pdev = to_platform_device(dev);
> > + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> > u32 itime;
> > int ret;
> > - ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
> > + ret = ds1374_read_bulk(ds1374_rtc->chip, &itime, DS1374_REG_TOD0, 4);
> > if (!ret)
> > rtc_time_to_tm(itime, time);
> > @@ -171,44 +89,47 @@ static int ds1374_read_time(struct device *dev, struct rtc_time *time)
> > static int ds1374_set_time(struct device *dev, struct rtc_time *time)
> > {
> > - struct i2c_client *client = to_i2c_client(dev);
> > + struct platform_device *pdev = to_platform_device(dev);
> > + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> > unsigned long itime;
> > rtc_tm_to_time(time, &itime);
> > - return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
> > + return ds1374_write_bulk(ds1374_rtc->chip, itime, DS1374_REG_TOD0, 4);
> > }
> > -#ifndef CONFIG_RTC_DRV_DS1374_WDT
> > /* The ds1374 has a decrementer for an alarm, rather than a comparator.
> > * If the time of day is changed, then the alarm will need to be
> > * reset.
> > */
> > static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> > {
> > - struct i2c_client *client = to_i2c_client(dev);
> > - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> > + struct platform_device *pdev = to_platform_device(dev);
> > + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> > + struct ds1374 *ds1374 = ds1374_rtc->chip;
> > +
> > u32 now, cur_alarm;
> > - int cr, sr;
> > + unsigned int cr, sr;
> > int ret = 0;
> > - if (client->irq <= 0)
> > + if (ds1374->irq <= 0)
> > return -EINVAL;
> > - mutex_lock(&ds1374->mutex);
> > + mutex_lock(&ds1374_rtc->mutex);
> > - cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> > + ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
> > if (ret < 0)
> > goto out;
> > - sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> > + ret = regmap_read(ds1374->regmap, DS1374_REG_SR, &sr);
> > if (ret < 0)
> > goto out;
> > - ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
> > + ret = ds1374_read_bulk(ds1374_rtc->chip, &now, DS1374_REG_TOD0, 4);
> > if (ret)
> > goto out;
> > - ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
> > + ret = ds1374_read_bulk(ds1374_rtc->chip, &cur_alarm,
> > + DS1374_REG_WDALM0, 3);
> > if (ret)
> > goto out;
> > @@ -217,20 +138,21 @@ static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> > alarm->pending = !!(sr & DS1374_REG_SR_AF);
> > out:
> > - mutex_unlock(&ds1374->mutex);
> > + mutex_unlock(&ds1374_rtc->mutex);
> > return ret;
> > }
> > static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> > {
> > - struct i2c_client *client = to_i2c_client(dev);
> > - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> > + struct platform_device *pdev = to_platform_device(dev);
> > + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> > + struct ds1374 *ds1374 = ds1374_rtc->chip;
> > +
> > struct rtc_time now;
> > unsigned long new_alarm, itime;
> > - int cr;
> > int ret = 0;
> > - if (client->irq <= 0)
> > + if (ds1374->irq <= 0)
> > return -EINVAL;
> > ret = ds1374_read_time(dev, &now);
> > @@ -251,468 +173,219 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> > else
> > new_alarm -= itime;
> > - mutex_lock(&ds1374->mutex);
> > -
> > - ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> > - if (ret < 0)
> > - goto out;
> > + mutex_lock(&ds1374_rtc->mutex);
> > /* Disable any existing alarm before setting the new one
> > - * (or lack thereof). */
> > - cr &= ~DS1374_REG_CR_WACE;
> > -
> > - ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
> > - if (ret < 0)
> > - goto out;
> > + * (or lack thereof).
> > + */
> > + ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
> > + DS1374_REG_CR_WACE, 0);
> > - ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
> > + ret = ds1374_write_bulk(ds1374_rtc->chip, new_alarm,
> > + DS1374_REG_WDALM0, 3);
> > if (ret)
> > goto out;
> > if (alarm->enabled) {
> > - cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
> > - cr &= ~DS1374_REG_CR_WDALM;
> > -
> > - ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
> > + ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
> > + DS1374_REG_CR_WACE | DS1374_REG_CR_AIE
> > + | DS1374_REG_CR_WDALM,
> > + DS1374_REG_CR_WACE
> > + | DS1374_REG_CR_AIE);
> > }
> > out:
> > - mutex_unlock(&ds1374->mutex);
> > + mutex_unlock(&ds1374_rtc->mutex);
> > return ret;
> > }
> > -#endif
> > static irqreturn_t ds1374_irq(int irq, void *dev_id)
> > {
> > - struct i2c_client *client = dev_id;
> > - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> > + struct ds1374_rtc *ds1374_rtc = dev_id;
> > disable_irq_nosync(irq);
> > - schedule_work(&ds1374->work);
> > + schedule_work(&ds1374_rtc->work);
> > return IRQ_HANDLED;
> > }
> > static void ds1374_work(struct work_struct *work)
> > {
> > - struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
> > - struct i2c_client *client = ds1374->client;
> > - int stat, control;
> > + struct ds1374_rtc *ds1374_rtc = container_of(work, struct ds1374_rtc,
> > + work);
> > + unsigned int stat;
> > + int ret;
> > - mutex_lock(&ds1374->mutex);
> > + mutex_lock(&ds1374_rtc->mutex);
> > - stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> > - if (stat < 0)
> > + ret = regmap_read(ds1374_rtc->chip->regmap, DS1374_REG_SR, &stat);
> > + if (ret)
> > goto unlock;
> > if (stat & DS1374_REG_SR_AF) {
> > - stat &= ~DS1374_REG_SR_AF;
> > - i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
> > -
> > - control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> > - if (control < 0)
> > + regmap_update_bits(ds1374_rtc->chip->regmap, DS1374_REG_SR,
> > + DS1374_REG_SR_AF, 0);
> > +
> > + ret = regmap_update_bits(ds1374_rtc->chip->regmap,
> > + DS1374_REG_CR, DS1374_REG_CR_WACE
> > + | DS1374_REG_CR_AIE,
> > + 0);
> > + if (ret)
> > goto out;
> > - control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
> > - i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
> > -
> > - rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
> > + rtc_update_irq(ds1374_rtc->rtc, 1, RTC_AF | RTC_IRQF);
> > }
> > out:
> > - if (!ds1374->exiting)
> > - enable_irq(client->irq);
> > + if (!ds1374_rtc->exiting)
> > + enable_irq(ds1374_rtc->chip->irq);
> > unlock:
> > - mutex_unlock(&ds1374->mutex);
> > + mutex_unlock(&ds1374_rtc->mutex);
> > }
> > -#ifndef CONFIG_RTC_DRV_DS1374_WDT
> > static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
> > {
> > - struct i2c_client *client = to_i2c_client(dev);
> > - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> > + struct platform_device *pdev = to_platform_device(dev);
> > + struct ds1374_rtc *ds1374 = platform_get_drvdata(pdev);
> > + unsigned int cr;
> > int ret;
> > mutex_lock(&ds1374->mutex);
> > - ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> > + ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &cr);
> > if (ret < 0)
> > goto out;
> > - if (enabled) {
> > - ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
> > - ret &= ~DS1374_REG_CR_WDALM;
> > - } else {
> > - ret &= ~DS1374_REG_CR_WACE;
> > - }
> > - ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
> > -
> > + if (enabled)
> > + regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
> > + DS1374_REG_CR_WACE | DS1374_REG_CR_AIE |
> > + DS1374_REG_CR_WDALM, DS1374_REG_CR_WACE |
> > + DS1374_REG_CR_AIE);
> > + else
> > + regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
> > + DS1374_REG_CR_WACE, 0);
> > out:
> > mutex_unlock(&ds1374->mutex);
> > return ret;
> > }
> > -#endif
> > -static const struct rtc_class_ops ds1374_rtc_ops = {
> > +static const struct rtc_class_ops ds1374_rtc_alm_ops = {
> > .read_time = ds1374_read_time,
> > .set_time = ds1374_set_time,
> > -#ifndef CONFIG_RTC_DRV_DS1374_WDT
> > .read_alarm = ds1374_read_alarm,
> > .set_alarm = ds1374_set_alarm,
> > .alarm_irq_enable = ds1374_alarm_irq_enable,
> > -#endif
> > };
> > -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> > -/*
> > - *****************************************************************************
> > - *
> > - * Watchdog Driver
> > - *
> > - *****************************************************************************
> > - */
> > -static struct i2c_client *save_client;
> > -/* Default margin */
> > -#define WD_TIMO 131762
> > -
> > -#define DRV_NAME "DS1374 Watchdog"
> > -
> > -static int wdt_margin = WD_TIMO;
> > -static unsigned long wdt_is_open;
> > -module_param(wdt_margin, int, 0);
> > -MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)");
> > -
> > -static const struct watchdog_info ds1374_wdt_info = {
> > - .identity = "DS1374 WTD",
> > - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
> > - WDIOF_MAGICCLOSE,
> > -};
> > -
> > -static int ds1374_wdt_settimeout(unsigned int timeout)
> > -{
> > - int ret = -ENOIOCTLCMD;
> > - int cr;
> > -
> > - ret = cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
> > - if (ret < 0)
> > - goto out;
> > -
> > - /* Disable any existing watchdog/alarm before setting the new one */
> > - cr &= ~DS1374_REG_CR_WACE;
> > -
> > - ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> > - if (ret < 0)
> > - goto out;
> > -
> > - /* Set new watchdog time */
> > - ret = ds1374_write_rtc(save_client, timeout, DS1374_REG_WDALM0, 3);
> > - if (ret) {
> > - pr_info("couldn't set new watchdog time\n");
> > - goto out;
> > - }
> > -
> > - /* Enable watchdog timer */
> > - cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM;
> > - cr &= ~DS1374_REG_CR_AIE;
> > -
> > - ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> > - if (ret < 0)
> > - goto out;
> > -
> > - return 0;
> > -out:
> > - return ret;
> > -}
> > -
> > -
> > -/*
> > - * Reload the watchdog timer. (ie, pat the watchdog)
> > - */
> > -static void ds1374_wdt_ping(void)
> > -{
> > - u32 val;
> > - int ret = 0;
> > -
> > - ret = ds1374_read_rtc(save_client, &val, DS1374_REG_WDALM0, 3);
> > - if (ret)
> > - pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret);
> > -}
> > -
> > -static void ds1374_wdt_disable(void)
> > -{
> > - int ret = -ENOIOCTLCMD;
> > - int cr;
> > -
> > - cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
> > - /* Disable watchdog timer */
> > - cr &= ~DS1374_REG_CR_WACE;
> > -
> > - ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> > -}
> > -
> > -/*
> > - * Watchdog device is opened, and watchdog starts running.
> > - */
> > -static int ds1374_wdt_open(struct inode *inode, struct file *file)
> > -{
> > - struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
> > -
> > - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
> > - mutex_lock(&ds1374->mutex);
> > - if (test_and_set_bit(0, &wdt_is_open)) {
> > - mutex_unlock(&ds1374->mutex);
> > - return -EBUSY;
> > - }
> > - /*
> > - * Activate
> > - */
> > - wdt_is_open = 1;
> > - mutex_unlock(&ds1374->mutex);
> > - return nonseekable_open(inode, file);
> > - }
> > - return -ENODEV;
> > -}
> > -
> > -/*
> > - * Close the watchdog device.
> > - */
> > -static int ds1374_wdt_release(struct inode *inode, struct file *file)
> > -{
> > - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
> > - clear_bit(0, &wdt_is_open);
> > -
> > - return 0;
> > -}
> > -
> > -/*
> > - * Pat the watchdog whenever device is written to.
> > - */
> > -static ssize_t ds1374_wdt_write(struct file *file, const char __user *data,
> > - size_t len, loff_t *ppos)
> > -{
> > - if (len) {
> > - ds1374_wdt_ping();
> > - return 1;
> > - }
> > - return 0;
> > -}
> > -
> > -static ssize_t ds1374_wdt_read(struct file *file, char __user *data,
> > - size_t len, loff_t *ppos)
> > -{
> > - return 0;
> > -}
> > -
> > -/*
> > - * Handle commands from user-space.
> > - */
> > -static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd,
> > - unsigned long arg)
> > -{
> > - int new_margin, options;
> > -
> > - switch (cmd) {
> > - case WDIOC_GETSUPPORT:
> > - return copy_to_user((struct watchdog_info __user *)arg,
> > - &ds1374_wdt_info, sizeof(ds1374_wdt_info)) ? -EFAULT : 0;
> > -
> > - case WDIOC_GETSTATUS:
> > - case WDIOC_GETBOOTSTATUS:
> > - return put_user(0, (int __user *)arg);
> > - case WDIOC_KEEPALIVE:
> > - ds1374_wdt_ping();
> > - return 0;
> > - case WDIOC_SETTIMEOUT:
> > - if (get_user(new_margin, (int __user *)arg))
> > - return -EFAULT;
> > -
> > - if (new_margin < 1 || new_margin > 16777216)
> > - return -EINVAL;
> > -
> > - wdt_margin = new_margin;
> > - ds1374_wdt_settimeout(new_margin);
> > - ds1374_wdt_ping();
> > - /* fallthrough */
> > - case WDIOC_GETTIMEOUT:
> > - return put_user(wdt_margin, (int __user *)arg);
> > - case WDIOC_SETOPTIONS:
> > - if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
> > - return -EFAULT;
> > -
> > - if (options & WDIOS_DISABLECARD) {
> > - pr_info("disable watchdog\n");
> > - ds1374_wdt_disable();
> > - }
> > -
> > - if (options & WDIOS_ENABLECARD) {
> > - pr_info("enable watchdog\n");
> > - ds1374_wdt_settimeout(wdt_margin);
> > - ds1374_wdt_ping();
> > - }
> > -
> > - return -EINVAL;
> > - }
> > - return -ENOTTY;
> > -}
> > -
> > -static long ds1374_wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
> > - unsigned long arg)
> > -{
> > - int ret;
> > - struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
> > -
> > - mutex_lock(&ds1374->mutex);
> > - ret = ds1374_wdt_ioctl(file, cmd, arg);
> > - mutex_unlock(&ds1374->mutex);
> > -
> > - return ret;
> > -}
> > -
> > -static int ds1374_wdt_notify_sys(struct notifier_block *this,
> > - unsigned long code, void *unused)
> > -{
> > - if (code == SYS_DOWN || code == SYS_HALT)
> > - /* Disable Watchdog */
> > - ds1374_wdt_disable();
> > - return NOTIFY_DONE;
> > -}
> > -
> > -static const struct file_operations ds1374_wdt_fops = {
> > - .owner = THIS_MODULE,
> > - .read = ds1374_wdt_read,
> > - .unlocked_ioctl = ds1374_wdt_unlocked_ioctl,
> > - .write = ds1374_wdt_write,
> > - .open = ds1374_wdt_open,
> > - .release = ds1374_wdt_release,
> > - .llseek = no_llseek,
> > -};
> > -
> > -static struct miscdevice ds1374_miscdev = {
> > - .minor = WATCHDOG_MINOR,
> > - .name = "watchdog",
> > - .fops = &ds1374_wdt_fops,
> > -};
> > -
> > -static struct notifier_block ds1374_wdt_notifier = {
> > - .notifier_call = ds1374_wdt_notify_sys,
> > +static const struct rtc_class_ops ds1374_rtc_ops = {
> > + .read_time = ds1374_read_time,
> > + .set_time = ds1374_set_time,
> > };
> > -#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
> > -/*
> > - *****************************************************************************
> > - *
> > - * Driver Interface
> > - *
> > - *****************************************************************************
> > - */
> > -static int ds1374_probe(struct i2c_client *client,
> > - const struct i2c_device_id *id)
> > +static int ds1374_rtc_probe(struct platform_device *pdev)
> > {
> > - struct ds1374 *ds1374;
> > + struct device *dev = &pdev->dev;
> > + struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
> > + struct ds1374_rtc *ds1374_rtc;
> > int ret;
> > - ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
> > - if (!ds1374)
> > + ds1374_rtc = devm_kzalloc(dev, sizeof(*ds1374_rtc), GFP_KERNEL);
> > + if (!ds1374_rtc)
> > return -ENOMEM;
> > + ds1374_rtc->chip = ds1374;
> > - ds1374->client = client;
> > - i2c_set_clientdata(client, ds1374);
> > + platform_set_drvdata(pdev, ds1374_rtc);
> > - INIT_WORK(&ds1374->work, ds1374_work);
> > - mutex_init(&ds1374->mutex);
> > + INIT_WORK(&ds1374_rtc->work, ds1374_work);
> > + mutex_init(&ds1374_rtc->mutex);
> > - ret = ds1374_check_rtc_status(client);
> > - if (ret)
> > + ret = ds1374_check_rtc_status(ds1374_rtc);
> > + if (ret) {
> > + dev_err(dev, "Failed to check rtc status\n");
> > return ret;
> > + }
> > - if (client->irq > 0) {
> > - ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
> > - "ds1374", client);
> > + /* if the mfd device indicates is configured to run with ALM
> > + * try to get the IRQ
> > + */
> > + if (ds1374->mode == DS1374_MODE_RTC_ALM && ds1374->irq > 0) {
> > + ret = devm_request_irq(dev, ds1374->irq,
> > + ds1374_irq, 0, "ds1374", ds1374_rtc);
> > if (ret) {
> > - dev_err(&client->dev, "unable to request IRQ\n");
> > + dev_err(dev, "unable to request IRQ\n");
> > return ret;
> > }
> > - device_set_wakeup_capable(&client->dev, 1);
> > + device_set_wakeup_capable(dev, 1);
> > + ds1374_rtc->rtc = devm_rtc_device_register(dev,
> > + "ds1374-rtc",
> > + &ds1374_rtc_alm_ops,
> > + THIS_MODULE);
> > + } else {
> > + ds1374_rtc->rtc = devm_rtc_device_register(dev, "ds1374-rtc",
> > + &ds1374_rtc_ops,
> > + THIS_MODULE);
> > }
> > - ds1374->rtc = devm_rtc_device_register(&client->dev, client->name,
> > - &ds1374_rtc_ops, THIS_MODULE);
> > - if (IS_ERR(ds1374->rtc)) {
> > - dev_err(&client->dev, "unable to register the class device\n");
> > - return PTR_ERR(ds1374->rtc);
> > + if (IS_ERR(ds1374_rtc->rtc)) {
> > + dev_err(dev, "unable to register the class device\n");
> > + return PTR_ERR(ds1374_rtc->rtc);
> > }
> > -
> > -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> > - save_client = client;
> > - ret = misc_register(&ds1374_miscdev);
> > - if (ret)
> > - return ret;
> > - ret = register_reboot_notifier(&ds1374_wdt_notifier);
> > - if (ret) {
> > - misc_deregister(&ds1374_miscdev);
> > - return ret;
> > - }
> > - ds1374_wdt_settimeout(131072);
> > -#endif
> > -
> > return 0;
> > }
> > -static int ds1374_remove(struct i2c_client *client)
> > +static int ds1374_rtc_remove(struct platform_device *pdev)
> > {
> > - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> > -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> > - misc_deregister(&ds1374_miscdev);
> > - ds1374_miscdev.parent = NULL;
> > - unregister_reboot_notifier(&ds1374_wdt_notifier);
> > -#endif
> > + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> > - if (client->irq > 0) {
> > - mutex_lock(&ds1374->mutex);
> > - ds1374->exiting = 1;
> > - mutex_unlock(&ds1374->mutex);
> > + if (ds1374_rtc->chip->irq > 0) {
> > + mutex_lock(&ds1374_rtc->mutex);
> > + ds1374_rtc->exiting = 1;
> > + mutex_unlock(&ds1374_rtc->mutex);
> > - devm_free_irq(&client->dev, client->irq, client);
> > - cancel_work_sync(&ds1374->work);
> > + devm_free_irq(&pdev->dev, ds1374_rtc->chip->irq,
> > + ds1374_rtc);
> > + cancel_work_sync(&ds1374_rtc->work);
> > }
> > return 0;
> > }
> > #ifdef CONFIG_PM_SLEEP
> > -static int ds1374_suspend(struct device *dev)
> > +static int ds1374_rtc_suspend(struct device *dev)
> > {
> > - struct i2c_client *client = to_i2c_client(dev);
> > + struct platform_device *pdev = to_platform_device(dev);
> > + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> > - if (client->irq > 0 && device_may_wakeup(&client->dev))
> > - enable_irq_wake(client->irq);
> > + if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
> > + enable_irq_wake(ds1374_rtc->chip->irq);
> > return 0;
> > }
> > -static int ds1374_resume(struct device *dev)
> > +static int ds1374_rtc_resume(struct device *dev)
> > {
> > - struct i2c_client *client = to_i2c_client(dev);
> > + struct platform_device *pdev = to_platform_device(dev);
> > + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> > - if (client->irq > 0 && device_may_wakeup(&client->dev))
> > - disable_irq_wake(client->irq);
> > + if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
> > + disable_irq_wake(ds1374_rtc->chip->irq);
> > return 0;
> > }
> > #endif
> > -static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
> > +static SIMPLE_DEV_PM_OPS(ds1374_rtc_pm, ds1374_rtc_suspend, ds1374_rtc_resume);
> > -static struct i2c_driver ds1374_driver = {
> > +static struct platform_driver ds1374_rtc_driver = {
> > .driver = {
> > - .name = "rtc-ds1374",
> > - .pm = &ds1374_pm,
> > + .name = "ds1374-rtc",
> > + .pm = &ds1374_rtc_pm,
> > },
> > - .probe = ds1374_probe,
> > - .remove = ds1374_remove,
> > - .id_table = ds1374_id,
> > + .probe = ds1374_rtc_probe,
> > + .remove = ds1374_rtc_remove,
> > };
> > -
> > -module_i2c_driver(ds1374_driver);
> > +module_platform_driver(ds1374_rtc_driver);
> > MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
> > +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> > MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
> > MODULE_LICENSE("GPL");
> > +MODULE_ALIAS("platform:ds1374-rtc");
> > diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> > index 52a70ee..1703611 100644
> > --- a/drivers/watchdog/Kconfig
> > +++ b/drivers/watchdog/Kconfig
> > @@ -120,6 +120,16 @@ config DA9062_WATCHDOG
> > This driver can be built as a module. The module name is da9062_wdt.
> > +config DS1374_WATCHDOG
> > + tristate "Maxim/Dallas 1374 Watchdog"
> > + depends on MFD_DS1374
> > + depends on REGMAP_I2C
>
> depends on I2C
> select REGMAP_I2C
>
> but doesn't the mfd driver already depend on that ?
Yeah, will fix that.
>
> > + select WATCHDOG_CORE
> > + help
> > + Support for the watchdog in the Maxim/Dallas DS1374 MFD.
> > +
> > + This driver can be built as a module. The module name is ds1374-wdt.
> > +
> > config GPIO_WATCHDOG
> > tristate "Watchdog device controlled through GPIO-line"
> > depends on OF_GPIO
> > diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> > index a2126e2..5b3b053 100644
> > --- a/drivers/watchdog/Makefile
> > +++ b/drivers/watchdog/Makefile
> > @@ -60,6 +60,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
> > obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
> > obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
> > obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
> > +obj-$(CONFIG_DS1374_WATCHDOG) += ds1374-wdt.o
> > obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
> > obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
> > obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
> > diff --git a/drivers/watchdog/ds1374-wdt.c b/drivers/watchdog/ds1374-wdt.c
> > new file mode 100644
> > index 0000000..d221560
> > --- /dev/null
> > +++ b/drivers/watchdog/ds1374-wdt.c
> > @@ -0,0 +1,208 @@
> > +/*
> > + * Copyright (c) 2017, National Instruments Corp.
> > + *
> > + * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
> > + * drivers/rtc/rtc-ds1374.c implementation
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + *
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/watchdog.h>
> > +#include <linux/slab.h>
> > +#include <linux/regmap.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/mfd/ds1374.h>
> > +
> alphabetic order, please.
Ok.
>
> > +#define DS1374_WDT_RATE 4096 /* Hz */
> > +#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
> > +#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
> > +
> Please use tabs
>
Will do.
> > +static bool nowayout = WATCHDOG_NOWAYOUT;
> > +module_param(nowayout, bool, 0444);
> > +MODULE_PARM_DESC(nowayout,
> > + "Watchdog cannot be stopped once started (default="
> > + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> > +
> > +static unsigned int timeout;
> > +module_param(timeout, int, 0444);
> > +MODULE_PARM_DESC(timeout, "Watchdog timeout");
> > +
> > +struct ds1374_wdt {
> > + struct ds1374 *chip;
> > + struct device *dev;
> > + struct watchdog_device wdd;
> > +};
> > +
> > +static int ds1374_wdt_stop(struct watchdog_device *wdog)
> > +{
> > + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> > + int err;
> > +
> > + err = regmap_update_bits(ds1374_wdt->chip->regmap, DS1374_REG_CR,
> > + DS1374_REG_CR_WACE, 0);
> > + if (err)
> > + return err;
> > +
> > + if (ds1374_wdt->chip->remapped_reset)
> > + return regmap_update_bits(ds1374_wdt->chip->regmap,
> > + DS1374_REG_CR, DS1374_REG_CR_WDSTR,
> > + 0);
> > +
> > + return 0;
> > +}
> > +
> > +static int ds1374_wdt_ping(struct watchdog_device *wdog)
> > +{
> > + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> > + u32 val;
> > + int err;
> > +
> > + err = ds1374_read_bulk(ds1374_wdt->chip, &val, DS1374_REG_WDALM0, 3);
>
> Why not just regmap_bulk_read() ?
Good catch.
>
> > + if (err < 0)
> > + return err;
> > +
> > + return 0;
> > +}
> > +
> > +static int ds1374_wdt_set_timeout(struct watchdog_device *wdog,
> > + unsigned int t)
> > +{
> > + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> > + struct regmap *regmap = ds1374_wdt->chip->regmap;
> > + unsigned int timeout = DS1374_WDT_RATE * t;
> > + u8 remapped = ds1374_wdt->chip->remapped_reset
> > + ? DS1374_REG_CR_WDSTR : 0;
>
> I personally prefer to split initialization from declaration if the initialization
> requires continuation lines.
Yeah, it's kludgy. Will redo it.
>
> > + int err;
> > +
> > + err = regmap_update_bits(regmap, DS1374_REG_CR,
> > + DS1374_REG_CR_WACE | DS1374_REG_CR_AIE, 0);
> > +
> > + err = ds1374_write_bulk(ds1374_wdt->chip, timeout,
> > + DS1374_REG_WDALM0, 3);
>
> Why not just regmap_bulk_write() ?
Agreed.
>
> The mixed use of mfd driver functions and regmap functions is confusing.
> I think it would be better to avoid it.
>
> > + if (err) {
> > + dev_err(ds1374_wdt->dev, "couldn't set new watchdog time\n");
>
> Is that noise necessary ? Same elsewhere. The error is returned to user space,
> which should be sufficient.
Ok, will get rid of it.
>
> > + return err;
> > + }
> > +
> > + ds1374_wdt->wdd.timeout = t;
>
> wdog->timeout = t;
>
> > +
> > + return regmap_update_bits(regmap, DS1374_REG_CR,
> > + (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
> > + DS1374_REG_CR_AIE | DS1374_REG_CR_WDSTR),
> > + (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
> > + DS1374_REG_CR_AIE | remapped));
>
> Some unnecessary ( )
Agreed.
>
> > +}
> > +
> > +static int ds1374_wdt_start(struct watchdog_device *wdog)
> > +{
> > + int err;
> > + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> > +
> > + err = ds1374_wdt_set_timeout(wdog, wdog->timeout);
> > + if (err) {
> > + dev_err(ds1374_wdt->dev, "%s: failed to set timeout (%d) %u\n",
> > + __func__, err, wdog->timeout);
> > + return err;
> > + }
> > +
> > + err = ds1374_wdt_ping(wdog);
> > + if (err) {
> > + dev_err(ds1374_wdt->dev, "%s: failed to ping (%d)\n", __func__,
> > + err);
I assume you'd want to get rid of that one too?
> > + return err;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct watchdog_info ds1374_wdt_info = {
> > + .identity = "DS1374 WTD",
> > + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> > + | WDIOF_MAGICCLOSE,
> > +};
> > +
> > +static const struct watchdog_ops ds1374_wdt_ops = {
> > + .owner = THIS_MODULE,
> > + .start = ds1374_wdt_start,
> > + .stop = ds1374_wdt_stop,
> > + .set_timeout = ds1374_wdt_set_timeout,
> > + .ping = ds1374_wdt_ping,
> > +};
> > +
> > +static int ds1374_wdt_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
> > + struct ds1374_wdt *priv;
> > + int err;
> > +
> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > + priv->chip = ds1374;
> > + platform_set_drvdata(pdev, priv);
> > +
> > + priv->wdd.info = &ds1374_wdt_info;
> > + priv->wdd.ops = &ds1374_wdt_ops;
> > + priv->wdd.min_timeout = DS1374_WDT_MIN_TIMEOUT;
> > + priv->wdd.timeout = DS1374_WDT_DEFAULT_TIMEOUT;
> > + priv->wdd.max_timeout = 0x1ffffff / DS1374_WDT_RATE;
> > + priv->wdd.parent = dev->parent;
> > +
> > + watchdog_init_timeout(&priv->wdd, timeout, dev);
> > + watchdog_set_nowayout(&priv->wdd, nowayout);
> > + watchdog_stop_on_reboot(&priv->wdd);
> > + watchdog_set_drvdata(&priv->wdd, priv);
> > +
> > + err = devm_watchdog_register_device(dev, &priv->wdd);
> > + if (err) {
> > + dev_err(dev, "Failed to register watchdog device\n");
> > + return err;
> > + }
> > +
> > + dev_info(dev, "Registered DS1374 Watchdog\n");
> > +
> > + return 0;
> > +}
> > +
> > +static int ds1374_wdt_remove(struct platform_device *pdev)
> > +{
> > + struct ds1374_wdt *priv = platform_get_drvdata(pdev);
> > +
> > + if (!nowayout)
> > + ds1374_wdt_stop(&priv->wdd);
> > +
>
> This may bypass MAGICCLOSE: If the watchdog daemon is killed and the module removed,
> the watchdog will be stopped. Is this really what you want ? If so, why set MAGICCLOSE
> in the first place ?
So your suggestion would be:
- if (!nowayout)
- ds1374_wdt_stop(&priv->wdd)
>
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int ds1374_wdt_suspend(struct device *dev)
> > +{
> > + return 0;
> > +}
> > +
> > +static int ds1374_wdt_resume(struct device *dev)
> > +{
> > + return 0;
> > +}
> > +#endif
> > +
> Those functions are quite pointless.
Agreed.
>
> > +static SIMPLE_DEV_PM_OPS(ds1374_wdt_pm, ds1374_wdt_suspend, ds1374_wdt_resume);
> > +
> > +static struct platform_driver ds1374_wdt_driver = {
> > + .probe = ds1374_wdt_probe,
> > + .remove = ds1374_wdt_remove,
> > + .driver = {
> > + .name = "ds1374-wdt",
> > + .pm = &ds1374_wdt_pm,
> > + },
> > +};
> > +module_platform_driver(ds1374_wdt_driver);
> > +
> > +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> > +MODULE_DESCRIPTION("Maxim/Dallas DS1374 WDT Driver");
> > +MODULE_LICENSE("GPL");
> > +MODULE_ALIAS("platform:ds1374-wdt");
> > diff --git a/include/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
> > new file mode 100644
> > index 0000000..b33cd5e
> > --- /dev/null
> > +++ b/include/dt-bindings/mfd/ds1374.h
> > @@ -0,0 +1,17 @@
> > +/*
> > + * This header provides macros for Maxim/Dallas DS1374 DT bindings
> > + *
> > + * Copyright (C) 2017 National Instruments Corp
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + *
> > + */
> > +
> > +#ifndef __DT_BINDINGS_MFD_DS1374_H__
> > +#define __DT_BINDINGS_MFD_DS1374_H__
> > +
> > +#define DALLAS_MODE_RTC 0
> > +#define DALLAS_MODE_ALM 1
> > +#define DALLAS_MODE_WDT 2
> > +
> > +#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
> > diff --git a/include/linux/mfd/ds1374.h b/include/linux/mfd/ds1374.h
> > new file mode 100644
> > index 0000000..7b697f8
> > --- /dev/null
> > +++ b/include/linux/mfd/ds1374.h
> > @@ -0,0 +1,59 @@
> > +/*
> > + * Copyright (c) 2017, National Instruments Corp.
> > + *
> > + * Multi Function Device for Dallas/Maxim DS1374 RTC/WDT
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; version 2 of the License.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef MFD_DS1374_H
> > +#define MFD_DS1374_H
> > +
> > +#include <linux/i2c.h>
> > +#include <linux/regmap.h>
> > +
> > +enum ds1374_mode {
> > + DS1374_MODE_RTC_ONLY,
> > + DS1374_MODE_RTC_ALM,
> > + DS1374_MODE_RTC_WDT,
> > +};
> > +
> > +/* Register definitions to for all subdrivers
> > + */
> > +#define DS1374_REG_TOD0 0x00 /* Time of Day */
> > +#define DS1374_REG_TOD1 0x01
> > +#define DS1374_REG_TOD2 0x02
> > +#define DS1374_REG_TOD3 0x03
> > +#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
> > +#define DS1374_REG_WDALM1 0x05
> > +#define DS1374_REG_WDALM2 0x06
> > +#define DS1374_REG_CR 0x07 /* Control */
> > +#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
> > +#define DS1374_REG_CR_WDSTR 0x08 /* 1=Reset on INT, 0=Rreset on RST */
> > +#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
> > +#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
> > +#define DS1374_REG_SR 0x08 /* Status */
> > +#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
> > +#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
> > +#define DS1374_REG_TCR 0x09 /* Trickle Charge */
> > +
> > +struct ds1374 {
> > + struct i2c_client *client;
> > + struct regmap *regmap;
> > + int irq;
> > + enum ds1374_mode mode;
> > + bool remapped_reset;
> > +};
> > +
> > +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes);
> > +
> > +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes);
> > +
> > +#endif /* MFD_DS1374_H */
> >
>
Thanks for your review!
Moritz
PS: I haven't forgotten about the cros-ec-hwmon, I'll get back to that at
one point ;-)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device
From: Guenter Roeck @ 2017-07-14 3:57 UTC (permalink / raw)
To: Moritz Fischer, lee.jones
Cc: robh+dt, mark.rutland, a.zummo, alexandre.belloni, wim,
devicetree, linux-kernel, linux-watchdog, linux-rtc,
Moritz Fischer
In-Reply-To: <1499975665-14581-2-git-send-email-mdf@kernel.org>
On 07/13/2017 12:54 PM, Moritz Fischer wrote:
> From: Moritz Fischer <moritz.fischer@ettus.com>
>
> Add support for the Maxim/Dallas DS1374 RTC/WDT with trickle charger.
> The device can either be configured as simple RTC, as simple RTC with
> Alarm (IRQ) as well as simple RTC with watchdog timer.
>
> Break up the old monolithic driver in drivers/rtc/rtc-ds1374.c into:
> - rtc part in drivers/rtc/rtc-ds1374.c
> - watchdog part under drivers/watchdog/ds1374-wdt.c
> - mfd part drivers/mfd/ds1374.c
>
> The MFD part takes care of trickle charging and mode selection,
> since the usage modes of a) RTC + Alarm or b) RTC + WDT
> are mutually exclusive.
>
> Signed-off-by: Moritz Fischer <mdf@kernel.org>
[ Only reviewing watchdog part ]
> ---
> drivers/mfd/Kconfig | 10 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/ds1374.c | 260 ++++++++++++++++
> drivers/rtc/rtc-ds1374.c | 639 ++++++++++-----------------------------
> drivers/watchdog/Kconfig | 10 +
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/ds1374-wdt.c | 208 +++++++++++++
> include/dt-bindings/mfd/ds1374.h | 17 ++
> include/linux/mfd/ds1374.h | 59 ++++
> 9 files changed, 722 insertions(+), 483 deletions(-)
> create mode 100644 drivers/mfd/ds1374.c
> create mode 100644 drivers/watchdog/ds1374-wdt.c
> create mode 100644 include/dt-bindings/mfd/ds1374.h
> create mode 100644 include/linux/mfd/ds1374.h
>
If possible, it might be better to split the patch into multiple parts, one per subsystem.
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3eb5c93..2dfef3c 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -203,6 +203,16 @@ config MFD_CROS_EC_SPI
> response time cannot be guaranteed, we support ignoring
> 'pre-amble' bytes before the response actually starts.
>
> +config MFD_DS1374
> + tristate "Dallas/Maxim DS1374 RTC/WDT/ALARM (I2C)"
> + select MFD_CORE
> + depends on I2C
> + depends on REGMAP_I2C
> +
> + ---help---
> + This driver supports the Dallas Maxim DS1374 multi function chip.
> + The chip combines an RTC, trickle charger, Watchdog or Alarm.
> +
> config MFD_ASIC3
> bool "Compaq ASIC3"
> depends on GPIOLIB && ARM
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index c16bf1e..b5cfcf4 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -15,6 +15,7 @@ cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o
> 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_DS1374) += ds1374.o
> obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
>
> rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
> diff --git a/drivers/mfd/ds1374.c b/drivers/mfd/ds1374.c
> new file mode 100644
> index 0000000..a0cfa1b
> --- /dev/null
> +++ b/drivers/mfd/ds1374.c
> @@ -0,0 +1,260 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Dallas/Maxim DS1374 Multi Function Device Driver
> + *
> + * The trickle charger code was taken more ore less 1:1 from
> + * drivers/rtc/rtc-1390.c
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/pm.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/ds1374.h>
> +
> +#define DS1374_TRICKLE_CHARGER_ENABLE 0xa0
> +#define DS1374_TRICKLE_CHARGER_ENABLE_MASK 0xe0
> +
> +#define DS1374_TRICKLE_CHARGER_250_OHM 0x01
> +#define DS1374_TRICKLE_CHARGER_2K_OHM 0x02
> +#define DS1374_TRICKLE_CHARGER_4K_OHM 0x03
> +#define DS1374_TRICKLE_CHARGER_ROUT_MASK 0x03
> +
> +#define DS1374_TRICKLE_CHARGER_NO_DIODE 0x04
> +#define DS1374_TRICKLE_CHARGER_DIODE 0x08
> +#define DS1374_TRICKLE_CHARGER_DIODE_MASK 0xc
> +
> +static const struct regmap_range volatile_ranges[] = {
> + regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_WDALM2),
> + regmap_reg_range(DS1374_REG_SR, DS1374_REG_SR),
> +};
> +
> +static const struct regmap_access_table ds1374_volatile_table = {
> + .yes_ranges = volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
> +};
> +
> +static struct regmap_config ds1374_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = DS1374_REG_TCR,
> + .volatile_table = &ds1374_volatile_table,
> + .cache_type = REGCACHE_RBTREE,
> +};
> +
> +static struct mfd_cell ds1374_wdt_cell = {
> + .name = "ds1374-wdt",
> +};
> +
> +static struct mfd_cell ds1374_rtc_cell = {
> + .name = "ds1374-rtc",
> +};
> +
> +static int ds1374_add_device(struct ds1374 *chip,
> + struct mfd_cell *cell)
> +{
> + cell->platform_data = chip;
> + cell->pdata_size = sizeof(*chip);
> +
> + return mfd_add_devices(&chip->client->dev, PLATFORM_DEVID_AUTO,
> + cell, 1, NULL, 0, NULL);
> +}
> +
> +static int ds1374_trickle_of_init(struct ds1374 *ds1374)
> +{
> + u32 ohms = 0;
> + u8 value;
> + struct i2c_client *client = ds1374->client;
> +
> + if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms",
> + &ohms))
> + return 0;
> +
> + /* Enable charger */
> + value = DS1374_TRICKLE_CHARGER_ENABLE;
> + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
> + value |= DS1374_TRICKLE_CHARGER_NO_DIODE;
> + else
> + value |= DS1374_TRICKLE_CHARGER_DIODE;
> +
> + /* Resistor select */
> + switch (ohms) {
> + case 250:
> + value |= DS1374_TRICKLE_CHARGER_250_OHM;
> + break;
> + case 2000:
> + value |= DS1374_TRICKLE_CHARGER_2K_OHM;
> + break;
> + case 4000:
> + value |= DS1374_TRICKLE_CHARGER_4K_OHM;
> + break;
> + default:
> + dev_warn(&client->dev,
> + "Unsupported ohm value %02ux in dt\n", ohms);
> + return -EINVAL;
> + }
> + dev_dbg(&client->dev, "Trickle charge value is 0x%02x\n", value);
> +
> + return regmap_write(ds1374->regmap, DS1374_REG_TCR, value);
> +}
> +
> +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes)
> +{
> + u8 buf[4];
> + int ret;
> + int i;
> +
> + if (WARN_ON(nbytes > 4))
> + return -EINVAL;
> +
> + ret = regmap_bulk_read(ds1374->regmap, reg, buf, nbytes);
> + if (ret) {
> + dev_err(&ds1374->client->dev,
> + "Failed to bulkread n = %d at R%d\n",
> + nbytes, reg);
> + return ret;
> + }
> +
> + for (i = nbytes - 1, *time = 0; i >= 0; i--)
> + *time = (*time << 8) | buf[i];
> +
I think those functions should go away; the calling code can use standard
endianness conversion functions and call regmap_bulk_{read,write} directly.
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(ds1374_read_bulk);
> +
> +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes)
> +{
> + u8 buf[4];
> + int i;
> +
> + if (nbytes > 4) {
> + WARN_ON(1);
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < nbytes; i++) {
> + buf[i] = time & 0xff;
> + time >>= 8;
> + }
> +
> + return regmap_bulk_write(ds1374->regmap, reg, buf, nbytes);
> +}
> +EXPORT_SYMBOL_GPL(ds1374_write_bulk);
> +
> +static int ds1374_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct ds1374 *ds1374;
> + u32 mode;
> + int err;
> +
> + ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
> + if (!ds1374)
> + return -ENOMEM;
> +
> + ds1374->regmap = devm_regmap_init_i2c(client, &ds1374_regmap_config);
> + if (IS_ERR(ds1374->regmap))
> + return PTR_ERR(ds1374->regmap);
> +
> + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
> + err = of_property_read_u32(client->dev.of_node,
> + "dallas,mode", &mode);
> + if (err < 0) {
> + dev_err(&client->dev, "missing dallas,mode property\n");
> + return -EINVAL;
> + }
> +
> + ds1374->remapped_reset
> + = of_property_read_bool(client->dev.of_node,
> + "dallas,remap-reset");
> +
> + ds1374->mode = (enum ds1374_mode)mode;
> + } else if (IS_ENABLED(CONFIG_RTC_DRV_DS1374_WDT)) {
> + ds1374->mode = DS1374_MODE_RTC_WDT;
> + } else {
> + ds1374->mode = DS1374_MODE_RTC_ALM;
> + }
> +
> + ds1374->client = client;
> + ds1374->irq = client->irq;
> + i2c_set_clientdata(client, ds1374);
> +
> + /* check if we're supposed to trickle charge */
> + err = ds1374_trickle_of_init(ds1374);
> + if (err) {
> + dev_err(&client->dev, "Failed to init trickle charger!\n");
> + return err;
> + }
> +
> + /* we always have a rtc */
> + err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
> + if (err)
> + return err;
> +
> + /* we might have a watchdog if configured that way */
> + if (ds1374->mode == DS1374_MODE_RTC_WDT)
> + return ds1374_add_device(ds1374, &ds1374_wdt_cell);
> +
> + return err;
> +}
> +
> +static const struct i2c_device_id ds1374_id[] = {
> + { "ds1374", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, ds1374_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id ds1374_of_match[] = {
> + { .compatible = "dallas,ds1374" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, ds1374_of_match);
> +#endif
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ds1374_suspend(struct device *dev)
> +{
> + return 0;
> +}
> +
> +static int ds1374_resume(struct device *dev)
> +{
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
> +
> +static struct i2c_driver ds1374_driver = {
> + .driver = {
> + .name = "ds1374",
> + .of_match_table = of_match_ptr(ds1374_of_match),
> + .pm = &ds1374_pm,
> + },
> + .probe = ds1374_probe,
> + .id_table = ds1374_id,
> +};
> +
> +static int __init ds1374_init(void)
> +{
> + return i2c_add_driver(&ds1374_driver);
> +}
> +subsys_initcall(ds1374_init);
> +
> +static void __exit ds1374_exit(void)
> +{
> + i2c_del_driver(&ds1374_driver);
> +}
> +module_exit(ds1374_exit);
> +
> +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> +MODULE_DESCRIPTION("Maxim/Dallas DS1374 MFD Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
> index 52429f0..29ce1a9 100644
> --- a/drivers/rtc/rtc-ds1374.c
> +++ b/drivers/rtc/rtc-ds1374.c
> @@ -1,75 +1,38 @@
> /*
> - * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
> + * RTC driver for the Maxim/Dallas DS1374 Real-Time Clock via MFD
> *
> * Based on code by Randy Vinson <rvinson@mvista.com>,
> * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
> *
> + * Copyright (C) 2017 National Instruments Corp
> * Copyright (C) 2014 Rose Technology
> * Copyright (C) 2006-2007 Freescale Semiconductor
> *
> + * SPDX-License-Identifier: GPL-2.0
> + *
> * 2005 (c) MontaVista Software, Inc. This file is licensed under
> * the terms of the GNU General Public License version 2. This program
> * is licensed "as is" without any warranty of any kind, whether express
> * or implied.
> */
> -/*
> - * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
> - * recommened in .../Documentation/i2c/writing-clients section
> - * "Sending and receiving", using SMBus level communication is preferred.
> - */
>
> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/interrupt.h>
> -#include <linux/i2c.h>
> #include <linux/rtc.h>
> #include <linux/bcd.h>
> #include <linux/workqueue.h>
> #include <linux/slab.h>
> #include <linux/pm.h>
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> -#include <linux/fs.h>
> -#include <linux/ioctl.h>
> -#include <linux/miscdevice.h>
> -#include <linux/reboot.h>
> -#include <linux/watchdog.h>
> -#endif
> -
> -#define DS1374_REG_TOD0 0x00 /* Time of Day */
> -#define DS1374_REG_TOD1 0x01
> -#define DS1374_REG_TOD2 0x02
> -#define DS1374_REG_TOD3 0x03
> -#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
> -#define DS1374_REG_WDALM1 0x05
> -#define DS1374_REG_WDALM2 0x06
> -#define DS1374_REG_CR 0x07 /* Control */
> -#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
> -#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
> -#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
> -#define DS1374_REG_SR 0x08 /* Status */
> -#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
> -#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
> -#define DS1374_REG_TCR 0x09 /* Trickle Charge */
> -
> -static const struct i2c_device_id ds1374_id[] = {
> - { "ds1374", 0 },
> - { }
> -};
> -MODULE_DEVICE_TABLE(i2c, ds1374_id);
> +#include <linux/regmap.h>
> +#include <linux/mfd/ds1374.h>
> +#include <linux/platform_device.h>
>
> -#ifdef CONFIG_OF
> -static const struct of_device_id ds1374_of_match[] = {
> - { .compatible = "dallas,ds1374" },
> - { }
> -};
> -MODULE_DEVICE_TABLE(of, ds1374_of_match);
> -#endif
> -
> -struct ds1374 {
> - struct i2c_client *client;
> +struct ds1374_rtc {
> struct rtc_device *rtc;
> + struct ds1374 *chip;
> struct work_struct work;
>
> /* The mutex protects alarm operations, and prevents a race
> @@ -80,89 +43,44 @@ struct ds1374 {
> int exiting;
> };
>
> -static struct i2c_driver ds1374_driver;
> -
> -static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
> - int reg, int nbytes)
> -{
> - u8 buf[4];
> - int ret;
> - int i;
> -
> - if (WARN_ON(nbytes > 4))
> - return -EINVAL;
> -
> - ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
> -
> - if (ret < 0)
> - return ret;
> - if (ret < nbytes)
> - return -EIO;
> -
> - for (i = nbytes - 1, *time = 0; i >= 0; i--)
> - *time = (*time << 8) | buf[i];
> -
> - return 0;
> -}
> -
> -static int ds1374_write_rtc(struct i2c_client *client, u32 time,
> - int reg, int nbytes)
> -{
> - u8 buf[4];
> - int i;
> -
> - if (nbytes > 4) {
> - WARN_ON(1);
> - return -EINVAL;
> - }
> -
> - for (i = 0; i < nbytes; i++) {
> - buf[i] = time & 0xff;
> - time >>= 8;
> - }
> -
> - return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
> -}
> -
> -static int ds1374_check_rtc_status(struct i2c_client *client)
> +static int ds1374_check_rtc_status(struct ds1374_rtc *ds1374)
> {
> int ret = 0;
> - int control, stat;
> + unsigned int control, stat;
>
> - stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> - if (stat < 0)
> + ret = regmap_read(ds1374->chip->regmap, DS1374_REG_SR, &stat);
> + if (ret)
> return stat;
>
> if (stat & DS1374_REG_SR_OSF)
> - dev_warn(&client->dev,
> + dev_warn(&ds1374->chip->client->dev,
> "oscillator discontinuity flagged, time unreliable\n");
>
> - stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
> -
> - ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
> - if (ret < 0)
> + ret = regmap_update_bits(ds1374->chip->regmap, DS1374_REG_SR,
> + DS1374_REG_SR_OSF | DS1374_REG_SR_AF, 0);
> + if (ret)
> return ret;
>
> /* If the alarm is pending, clear it before requesting
> * the interrupt, so an interrupt event isn't reported
> * before everything is initialized.
> */
> -
> - control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> - if (control < 0)
> - return control;
> + ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &control);
> + if (ret)
> + return ret;
>
> control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
> - return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
> + return regmap_write(ds1374->chip->regmap, DS1374_REG_CR, control);
> }
>
> static int ds1374_read_time(struct device *dev, struct rtc_time *time)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> u32 itime;
> int ret;
>
> - ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
> + ret = ds1374_read_bulk(ds1374_rtc->chip, &itime, DS1374_REG_TOD0, 4);
> if (!ret)
> rtc_time_to_tm(itime, time);
>
> @@ -171,44 +89,47 @@ static int ds1374_read_time(struct device *dev, struct rtc_time *time)
>
> static int ds1374_set_time(struct device *dev, struct rtc_time *time)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> unsigned long itime;
>
> rtc_tm_to_time(time, &itime);
> - return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
> + return ds1374_write_bulk(ds1374_rtc->chip, itime, DS1374_REG_TOD0, 4);
> }
>
> -#ifndef CONFIG_RTC_DRV_DS1374_WDT
> /* The ds1374 has a decrementer for an alarm, rather than a comparator.
> * If the time of day is changed, then the alarm will need to be
> * reset.
> */
> static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> + struct ds1374 *ds1374 = ds1374_rtc->chip;
> +
> u32 now, cur_alarm;
> - int cr, sr;
> + unsigned int cr, sr;
> int ret = 0;
>
> - if (client->irq <= 0)
> + if (ds1374->irq <= 0)
> return -EINVAL;
>
> - mutex_lock(&ds1374->mutex);
> + mutex_lock(&ds1374_rtc->mutex);
>
> - cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> + ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
> if (ret < 0)
> goto out;
>
> - sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> + ret = regmap_read(ds1374->regmap, DS1374_REG_SR, &sr);
> if (ret < 0)
> goto out;
>
> - ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
> + ret = ds1374_read_bulk(ds1374_rtc->chip, &now, DS1374_REG_TOD0, 4);
> if (ret)
> goto out;
>
> - ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
> + ret = ds1374_read_bulk(ds1374_rtc->chip, &cur_alarm,
> + DS1374_REG_WDALM0, 3);
> if (ret)
> goto out;
>
> @@ -217,20 +138,21 @@ static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> alarm->pending = !!(sr & DS1374_REG_SR_AF);
>
> out:
> - mutex_unlock(&ds1374->mutex);
> + mutex_unlock(&ds1374_rtc->mutex);
> return ret;
> }
>
> static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> + struct ds1374 *ds1374 = ds1374_rtc->chip;
> +
> struct rtc_time now;
> unsigned long new_alarm, itime;
> - int cr;
> int ret = 0;
>
> - if (client->irq <= 0)
> + if (ds1374->irq <= 0)
> return -EINVAL;
>
> ret = ds1374_read_time(dev, &now);
> @@ -251,468 +173,219 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> else
> new_alarm -= itime;
>
> - mutex_lock(&ds1374->mutex);
> -
> - ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> - if (ret < 0)
> - goto out;
> + mutex_lock(&ds1374_rtc->mutex);
>
> /* Disable any existing alarm before setting the new one
> - * (or lack thereof). */
> - cr &= ~DS1374_REG_CR_WACE;
> -
> - ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
> - if (ret < 0)
> - goto out;
> + * (or lack thereof).
> + */
> + ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
> + DS1374_REG_CR_WACE, 0);
>
> - ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
> + ret = ds1374_write_bulk(ds1374_rtc->chip, new_alarm,
> + DS1374_REG_WDALM0, 3);
> if (ret)
> goto out;
>
> if (alarm->enabled) {
> - cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
> - cr &= ~DS1374_REG_CR_WDALM;
> -
> - ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
> + ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
> + DS1374_REG_CR_WACE | DS1374_REG_CR_AIE
> + | DS1374_REG_CR_WDALM,
> + DS1374_REG_CR_WACE
> + | DS1374_REG_CR_AIE);
> }
>
> out:
> - mutex_unlock(&ds1374->mutex);
> + mutex_unlock(&ds1374_rtc->mutex);
> return ret;
> }
> -#endif
>
> static irqreturn_t ds1374_irq(int irq, void *dev_id)
> {
> - struct i2c_client *client = dev_id;
> - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> + struct ds1374_rtc *ds1374_rtc = dev_id;
>
> disable_irq_nosync(irq);
> - schedule_work(&ds1374->work);
> + schedule_work(&ds1374_rtc->work);
> return IRQ_HANDLED;
> }
>
> static void ds1374_work(struct work_struct *work)
> {
> - struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
> - struct i2c_client *client = ds1374->client;
> - int stat, control;
> + struct ds1374_rtc *ds1374_rtc = container_of(work, struct ds1374_rtc,
> + work);
> + unsigned int stat;
> + int ret;
>
> - mutex_lock(&ds1374->mutex);
> + mutex_lock(&ds1374_rtc->mutex);
>
> - stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> - if (stat < 0)
> + ret = regmap_read(ds1374_rtc->chip->regmap, DS1374_REG_SR, &stat);
> + if (ret)
> goto unlock;
>
> if (stat & DS1374_REG_SR_AF) {
> - stat &= ~DS1374_REG_SR_AF;
> - i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
> -
> - control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> - if (control < 0)
> + regmap_update_bits(ds1374_rtc->chip->regmap, DS1374_REG_SR,
> + DS1374_REG_SR_AF, 0);
> +
> + ret = regmap_update_bits(ds1374_rtc->chip->regmap,
> + DS1374_REG_CR, DS1374_REG_CR_WACE
> + | DS1374_REG_CR_AIE,
> + 0);
> + if (ret)
> goto out;
>
> - control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
> - i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
> -
> - rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
> + rtc_update_irq(ds1374_rtc->rtc, 1, RTC_AF | RTC_IRQF);
> }
>
> out:
> - if (!ds1374->exiting)
> - enable_irq(client->irq);
> + if (!ds1374_rtc->exiting)
> + enable_irq(ds1374_rtc->chip->irq);
> unlock:
> - mutex_unlock(&ds1374->mutex);
> + mutex_unlock(&ds1374_rtc->mutex);
> }
>
> -#ifndef CONFIG_RTC_DRV_DS1374_WDT
> static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ds1374_rtc *ds1374 = platform_get_drvdata(pdev);
> + unsigned int cr;
> int ret;
>
> mutex_lock(&ds1374->mutex);
>
> - ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> + ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &cr);
> if (ret < 0)
> goto out;
>
> - if (enabled) {
> - ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
> - ret &= ~DS1374_REG_CR_WDALM;
> - } else {
> - ret &= ~DS1374_REG_CR_WACE;
> - }
> - ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
> -
> + if (enabled)
> + regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
> + DS1374_REG_CR_WACE | DS1374_REG_CR_AIE |
> + DS1374_REG_CR_WDALM, DS1374_REG_CR_WACE |
> + DS1374_REG_CR_AIE);
> + else
> + regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
> + DS1374_REG_CR_WACE, 0);
> out:
> mutex_unlock(&ds1374->mutex);
> return ret;
> }
> -#endif
>
> -static const struct rtc_class_ops ds1374_rtc_ops = {
> +static const struct rtc_class_ops ds1374_rtc_alm_ops = {
> .read_time = ds1374_read_time,
> .set_time = ds1374_set_time,
> -#ifndef CONFIG_RTC_DRV_DS1374_WDT
> .read_alarm = ds1374_read_alarm,
> .set_alarm = ds1374_set_alarm,
> .alarm_irq_enable = ds1374_alarm_irq_enable,
> -#endif
> };
>
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> -/*
> - *****************************************************************************
> - *
> - * Watchdog Driver
> - *
> - *****************************************************************************
> - */
> -static struct i2c_client *save_client;
> -/* Default margin */
> -#define WD_TIMO 131762
> -
> -#define DRV_NAME "DS1374 Watchdog"
> -
> -static int wdt_margin = WD_TIMO;
> -static unsigned long wdt_is_open;
> -module_param(wdt_margin, int, 0);
> -MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)");
> -
> -static const struct watchdog_info ds1374_wdt_info = {
> - .identity = "DS1374 WTD",
> - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
> - WDIOF_MAGICCLOSE,
> -};
> -
> -static int ds1374_wdt_settimeout(unsigned int timeout)
> -{
> - int ret = -ENOIOCTLCMD;
> - int cr;
> -
> - ret = cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
> - if (ret < 0)
> - goto out;
> -
> - /* Disable any existing watchdog/alarm before setting the new one */
> - cr &= ~DS1374_REG_CR_WACE;
> -
> - ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> - if (ret < 0)
> - goto out;
> -
> - /* Set new watchdog time */
> - ret = ds1374_write_rtc(save_client, timeout, DS1374_REG_WDALM0, 3);
> - if (ret) {
> - pr_info("couldn't set new watchdog time\n");
> - goto out;
> - }
> -
> - /* Enable watchdog timer */
> - cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM;
> - cr &= ~DS1374_REG_CR_AIE;
> -
> - ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> - if (ret < 0)
> - goto out;
> -
> - return 0;
> -out:
> - return ret;
> -}
> -
> -
> -/*
> - * Reload the watchdog timer. (ie, pat the watchdog)
> - */
> -static void ds1374_wdt_ping(void)
> -{
> - u32 val;
> - int ret = 0;
> -
> - ret = ds1374_read_rtc(save_client, &val, DS1374_REG_WDALM0, 3);
> - if (ret)
> - pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret);
> -}
> -
> -static void ds1374_wdt_disable(void)
> -{
> - int ret = -ENOIOCTLCMD;
> - int cr;
> -
> - cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
> - /* Disable watchdog timer */
> - cr &= ~DS1374_REG_CR_WACE;
> -
> - ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> -}
> -
> -/*
> - * Watchdog device is opened, and watchdog starts running.
> - */
> -static int ds1374_wdt_open(struct inode *inode, struct file *file)
> -{
> - struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
> -
> - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
> - mutex_lock(&ds1374->mutex);
> - if (test_and_set_bit(0, &wdt_is_open)) {
> - mutex_unlock(&ds1374->mutex);
> - return -EBUSY;
> - }
> - /*
> - * Activate
> - */
> - wdt_is_open = 1;
> - mutex_unlock(&ds1374->mutex);
> - return nonseekable_open(inode, file);
> - }
> - return -ENODEV;
> -}
> -
> -/*
> - * Close the watchdog device.
> - */
> -static int ds1374_wdt_release(struct inode *inode, struct file *file)
> -{
> - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
> - clear_bit(0, &wdt_is_open);
> -
> - return 0;
> -}
> -
> -/*
> - * Pat the watchdog whenever device is written to.
> - */
> -static ssize_t ds1374_wdt_write(struct file *file, const char __user *data,
> - size_t len, loff_t *ppos)
> -{
> - if (len) {
> - ds1374_wdt_ping();
> - return 1;
> - }
> - return 0;
> -}
> -
> -static ssize_t ds1374_wdt_read(struct file *file, char __user *data,
> - size_t len, loff_t *ppos)
> -{
> - return 0;
> -}
> -
> -/*
> - * Handle commands from user-space.
> - */
> -static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd,
> - unsigned long arg)
> -{
> - int new_margin, options;
> -
> - switch (cmd) {
> - case WDIOC_GETSUPPORT:
> - return copy_to_user((struct watchdog_info __user *)arg,
> - &ds1374_wdt_info, sizeof(ds1374_wdt_info)) ? -EFAULT : 0;
> -
> - case WDIOC_GETSTATUS:
> - case WDIOC_GETBOOTSTATUS:
> - return put_user(0, (int __user *)arg);
> - case WDIOC_KEEPALIVE:
> - ds1374_wdt_ping();
> - return 0;
> - case WDIOC_SETTIMEOUT:
> - if (get_user(new_margin, (int __user *)arg))
> - return -EFAULT;
> -
> - if (new_margin < 1 || new_margin > 16777216)
> - return -EINVAL;
> -
> - wdt_margin = new_margin;
> - ds1374_wdt_settimeout(new_margin);
> - ds1374_wdt_ping();
> - /* fallthrough */
> - case WDIOC_GETTIMEOUT:
> - return put_user(wdt_margin, (int __user *)arg);
> - case WDIOC_SETOPTIONS:
> - if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
> - return -EFAULT;
> -
> - if (options & WDIOS_DISABLECARD) {
> - pr_info("disable watchdog\n");
> - ds1374_wdt_disable();
> - }
> -
> - if (options & WDIOS_ENABLECARD) {
> - pr_info("enable watchdog\n");
> - ds1374_wdt_settimeout(wdt_margin);
> - ds1374_wdt_ping();
> - }
> -
> - return -EINVAL;
> - }
> - return -ENOTTY;
> -}
> -
> -static long ds1374_wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
> - unsigned long arg)
> -{
> - int ret;
> - struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
> -
> - mutex_lock(&ds1374->mutex);
> - ret = ds1374_wdt_ioctl(file, cmd, arg);
> - mutex_unlock(&ds1374->mutex);
> -
> - return ret;
> -}
> -
> -static int ds1374_wdt_notify_sys(struct notifier_block *this,
> - unsigned long code, void *unused)
> -{
> - if (code == SYS_DOWN || code == SYS_HALT)
> - /* Disable Watchdog */
> - ds1374_wdt_disable();
> - return NOTIFY_DONE;
> -}
> -
> -static const struct file_operations ds1374_wdt_fops = {
> - .owner = THIS_MODULE,
> - .read = ds1374_wdt_read,
> - .unlocked_ioctl = ds1374_wdt_unlocked_ioctl,
> - .write = ds1374_wdt_write,
> - .open = ds1374_wdt_open,
> - .release = ds1374_wdt_release,
> - .llseek = no_llseek,
> -};
> -
> -static struct miscdevice ds1374_miscdev = {
> - .minor = WATCHDOG_MINOR,
> - .name = "watchdog",
> - .fops = &ds1374_wdt_fops,
> -};
> -
> -static struct notifier_block ds1374_wdt_notifier = {
> - .notifier_call = ds1374_wdt_notify_sys,
> +static const struct rtc_class_ops ds1374_rtc_ops = {
> + .read_time = ds1374_read_time,
> + .set_time = ds1374_set_time,
> };
>
> -#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
> -/*
> - *****************************************************************************
> - *
> - * Driver Interface
> - *
> - *****************************************************************************
> - */
> -static int ds1374_probe(struct i2c_client *client,
> - const struct i2c_device_id *id)
> +static int ds1374_rtc_probe(struct platform_device *pdev)
> {
> - struct ds1374 *ds1374;
> + struct device *dev = &pdev->dev;
> + struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
> + struct ds1374_rtc *ds1374_rtc;
> int ret;
>
> - ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
> - if (!ds1374)
> + ds1374_rtc = devm_kzalloc(dev, sizeof(*ds1374_rtc), GFP_KERNEL);
> + if (!ds1374_rtc)
> return -ENOMEM;
> + ds1374_rtc->chip = ds1374;
>
> - ds1374->client = client;
> - i2c_set_clientdata(client, ds1374);
> + platform_set_drvdata(pdev, ds1374_rtc);
>
> - INIT_WORK(&ds1374->work, ds1374_work);
> - mutex_init(&ds1374->mutex);
> + INIT_WORK(&ds1374_rtc->work, ds1374_work);
> + mutex_init(&ds1374_rtc->mutex);
>
> - ret = ds1374_check_rtc_status(client);
> - if (ret)
> + ret = ds1374_check_rtc_status(ds1374_rtc);
> + if (ret) {
> + dev_err(dev, "Failed to check rtc status\n");
> return ret;
> + }
>
> - if (client->irq > 0) {
> - ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
> - "ds1374", client);
> + /* if the mfd device indicates is configured to run with ALM
> + * try to get the IRQ
> + */
> + if (ds1374->mode == DS1374_MODE_RTC_ALM && ds1374->irq > 0) {
> + ret = devm_request_irq(dev, ds1374->irq,
> + ds1374_irq, 0, "ds1374", ds1374_rtc);
> if (ret) {
> - dev_err(&client->dev, "unable to request IRQ\n");
> + dev_err(dev, "unable to request IRQ\n");
> return ret;
> }
>
> - device_set_wakeup_capable(&client->dev, 1);
> + device_set_wakeup_capable(dev, 1);
> + ds1374_rtc->rtc = devm_rtc_device_register(dev,
> + "ds1374-rtc",
> + &ds1374_rtc_alm_ops,
> + THIS_MODULE);
> + } else {
> + ds1374_rtc->rtc = devm_rtc_device_register(dev, "ds1374-rtc",
> + &ds1374_rtc_ops,
> + THIS_MODULE);
> }
>
> - ds1374->rtc = devm_rtc_device_register(&client->dev, client->name,
> - &ds1374_rtc_ops, THIS_MODULE);
> - if (IS_ERR(ds1374->rtc)) {
> - dev_err(&client->dev, "unable to register the class device\n");
> - return PTR_ERR(ds1374->rtc);
> + if (IS_ERR(ds1374_rtc->rtc)) {
> + dev_err(dev, "unable to register the class device\n");
> + return PTR_ERR(ds1374_rtc->rtc);
> }
> -
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> - save_client = client;
> - ret = misc_register(&ds1374_miscdev);
> - if (ret)
> - return ret;
> - ret = register_reboot_notifier(&ds1374_wdt_notifier);
> - if (ret) {
> - misc_deregister(&ds1374_miscdev);
> - return ret;
> - }
> - ds1374_wdt_settimeout(131072);
> -#endif
> -
> return 0;
> }
>
> -static int ds1374_remove(struct i2c_client *client)
> +static int ds1374_rtc_remove(struct platform_device *pdev)
> {
> - struct ds1374 *ds1374 = i2c_get_clientdata(client);
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> - misc_deregister(&ds1374_miscdev);
> - ds1374_miscdev.parent = NULL;
> - unregister_reboot_notifier(&ds1374_wdt_notifier);
> -#endif
> + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>
> - if (client->irq > 0) {
> - mutex_lock(&ds1374->mutex);
> - ds1374->exiting = 1;
> - mutex_unlock(&ds1374->mutex);
> + if (ds1374_rtc->chip->irq > 0) {
> + mutex_lock(&ds1374_rtc->mutex);
> + ds1374_rtc->exiting = 1;
> + mutex_unlock(&ds1374_rtc->mutex);
>
> - devm_free_irq(&client->dev, client->irq, client);
> - cancel_work_sync(&ds1374->work);
> + devm_free_irq(&pdev->dev, ds1374_rtc->chip->irq,
> + ds1374_rtc);
> + cancel_work_sync(&ds1374_rtc->work);
> }
>
> return 0;
> }
>
> #ifdef CONFIG_PM_SLEEP
> -static int ds1374_suspend(struct device *dev)
> +static int ds1374_rtc_suspend(struct device *dev)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>
> - if (client->irq > 0 && device_may_wakeup(&client->dev))
> - enable_irq_wake(client->irq);
> + if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
> + enable_irq_wake(ds1374_rtc->chip->irq);
> return 0;
> }
>
> -static int ds1374_resume(struct device *dev)
> +static int ds1374_rtc_resume(struct device *dev)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>
> - if (client->irq > 0 && device_may_wakeup(&client->dev))
> - disable_irq_wake(client->irq);
> + if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
> + disable_irq_wake(ds1374_rtc->chip->irq);
> return 0;
> }
> #endif
>
> -static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
> +static SIMPLE_DEV_PM_OPS(ds1374_rtc_pm, ds1374_rtc_suspend, ds1374_rtc_resume);
>
> -static struct i2c_driver ds1374_driver = {
> +static struct platform_driver ds1374_rtc_driver = {
> .driver = {
> - .name = "rtc-ds1374",
> - .pm = &ds1374_pm,
> + .name = "ds1374-rtc",
> + .pm = &ds1374_rtc_pm,
> },
> - .probe = ds1374_probe,
> - .remove = ds1374_remove,
> - .id_table = ds1374_id,
> + .probe = ds1374_rtc_probe,
> + .remove = ds1374_rtc_remove,
> };
> -
> -module_i2c_driver(ds1374_driver);
> +module_platform_driver(ds1374_rtc_driver);
>
> MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
> +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
> MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:ds1374-rtc");
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 52a70ee..1703611 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -120,6 +120,16 @@ config DA9062_WATCHDOG
>
> This driver can be built as a module. The module name is da9062_wdt.
>
> +config DS1374_WATCHDOG
> + tristate "Maxim/Dallas 1374 Watchdog"
> + depends on MFD_DS1374
> + depends on REGMAP_I2C
depends on I2C
select REGMAP_I2C
but doesn't the mfd driver already depend on that ?
> + select WATCHDOG_CORE
> + help
> + Support for the watchdog in the Maxim/Dallas DS1374 MFD.
> +
> + This driver can be built as a module. The module name is ds1374-wdt.
> +
> config GPIO_WATCHDOG
> tristate "Watchdog device controlled through GPIO-line"
> depends on OF_GPIO
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index a2126e2..5b3b053 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -60,6 +60,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
> obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
> obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
> obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
> +obj-$(CONFIG_DS1374_WATCHDOG) += ds1374-wdt.o
> obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
> obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
> obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
> diff --git a/drivers/watchdog/ds1374-wdt.c b/drivers/watchdog/ds1374-wdt.c
> new file mode 100644
> index 0000000..d221560
> --- /dev/null
> +++ b/drivers/watchdog/ds1374-wdt.c
> @@ -0,0 +1,208 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
> + * drivers/rtc/rtc-ds1374.c implementation
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/watchdog.h>
> +#include <linux/slab.h>
> +#include <linux/regmap.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/ds1374.h>
> +
alphabetic order, please.
> +#define DS1374_WDT_RATE 4096 /* Hz */
> +#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
> +#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
> +
Please use tabs
> +static bool nowayout = WATCHDOG_NOWAYOUT;
> +module_param(nowayout, bool, 0444);
> +MODULE_PARM_DESC(nowayout,
> + "Watchdog cannot be stopped once started (default="
> + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> +
> +static unsigned int timeout;
> +module_param(timeout, int, 0444);
> +MODULE_PARM_DESC(timeout, "Watchdog timeout");
> +
> +struct ds1374_wdt {
> + struct ds1374 *chip;
> + struct device *dev;
> + struct watchdog_device wdd;
> +};
> +
> +static int ds1374_wdt_stop(struct watchdog_device *wdog)
> +{
> + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> + int err;
> +
> + err = regmap_update_bits(ds1374_wdt->chip->regmap, DS1374_REG_CR,
> + DS1374_REG_CR_WACE, 0);
> + if (err)
> + return err;
> +
> + if (ds1374_wdt->chip->remapped_reset)
> + return regmap_update_bits(ds1374_wdt->chip->regmap,
> + DS1374_REG_CR, DS1374_REG_CR_WDSTR,
> + 0);
> +
> + return 0;
> +}
> +
> +static int ds1374_wdt_ping(struct watchdog_device *wdog)
> +{
> + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> + u32 val;
> + int err;
> +
> + err = ds1374_read_bulk(ds1374_wdt->chip, &val, DS1374_REG_WDALM0, 3);
Why not just regmap_bulk_read() ?
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> +static int ds1374_wdt_set_timeout(struct watchdog_device *wdog,
> + unsigned int t)
> +{
> + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> + struct regmap *regmap = ds1374_wdt->chip->regmap;
> + unsigned int timeout = DS1374_WDT_RATE * t;
> + u8 remapped = ds1374_wdt->chip->remapped_reset
> + ? DS1374_REG_CR_WDSTR : 0;
I personally prefer to split initialization from declaration if the initialization
requires continuation lines.
> + int err;
> +
> + err = regmap_update_bits(regmap, DS1374_REG_CR,
> + DS1374_REG_CR_WACE | DS1374_REG_CR_AIE, 0);
> +
> + err = ds1374_write_bulk(ds1374_wdt->chip, timeout,
> + DS1374_REG_WDALM0, 3);
Why not just regmap_bulk_write() ?
The mixed use of mfd driver functions and regmap functions is confusing.
I think it would be better to avoid it.
> + if (err) {
> + dev_err(ds1374_wdt->dev, "couldn't set new watchdog time\n");
Is that noise necessary ? Same elsewhere. The error is returned to user space,
which should be sufficient.
> + return err;
> + }
> +
> + ds1374_wdt->wdd.timeout = t;
wdog->timeout = t;
> +
> + return regmap_update_bits(regmap, DS1374_REG_CR,
> + (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
> + DS1374_REG_CR_AIE | DS1374_REG_CR_WDSTR),
> + (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
> + DS1374_REG_CR_AIE | remapped));
Some unnecessary ( )
> +}
> +
> +static int ds1374_wdt_start(struct watchdog_device *wdog)
> +{
> + int err;
> + struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> +
> + err = ds1374_wdt_set_timeout(wdog, wdog->timeout);
> + if (err) {
> + dev_err(ds1374_wdt->dev, "%s: failed to set timeout (%d) %u\n",
> + __func__, err, wdog->timeout);
> + return err;
> + }
> +
> + err = ds1374_wdt_ping(wdog);
> + if (err) {
> + dev_err(ds1374_wdt->dev, "%s: failed to ping (%d)\n", __func__,
> + err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static const struct watchdog_info ds1374_wdt_info = {
> + .identity = "DS1374 WTD",
> + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> + | WDIOF_MAGICCLOSE,
> +};
> +
> +static const struct watchdog_ops ds1374_wdt_ops = {
> + .owner = THIS_MODULE,
> + .start = ds1374_wdt_start,
> + .stop = ds1374_wdt_stop,
> + .set_timeout = ds1374_wdt_set_timeout,
> + .ping = ds1374_wdt_ping,
> +};
> +
> +static int ds1374_wdt_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
> + struct ds1374_wdt *priv;
> + int err;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> + priv->chip = ds1374;
> + platform_set_drvdata(pdev, priv);
> +
> + priv->wdd.info = &ds1374_wdt_info;
> + priv->wdd.ops = &ds1374_wdt_ops;
> + priv->wdd.min_timeout = DS1374_WDT_MIN_TIMEOUT;
> + priv->wdd.timeout = DS1374_WDT_DEFAULT_TIMEOUT;
> + priv->wdd.max_timeout = 0x1ffffff / DS1374_WDT_RATE;
> + priv->wdd.parent = dev->parent;
> +
> + watchdog_init_timeout(&priv->wdd, timeout, dev);
> + watchdog_set_nowayout(&priv->wdd, nowayout);
> + watchdog_stop_on_reboot(&priv->wdd);
> + watchdog_set_drvdata(&priv->wdd, priv);
> +
> + err = devm_watchdog_register_device(dev, &priv->wdd);
> + if (err) {
> + dev_err(dev, "Failed to register watchdog device\n");
> + return err;
> + }
> +
> + dev_info(dev, "Registered DS1374 Watchdog\n");
> +
> + return 0;
> +}
> +
> +static int ds1374_wdt_remove(struct platform_device *pdev)
> +{
> + struct ds1374_wdt *priv = platform_get_drvdata(pdev);
> +
> + if (!nowayout)
> + ds1374_wdt_stop(&priv->wdd);
> +
This may bypass MAGICCLOSE: If the watchdog daemon is killed and the module removed,
the watchdog will be stopped. Is this really what you want ? If so, why set MAGICCLOSE
in the first place ?
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ds1374_wdt_suspend(struct device *dev)
> +{
> + return 0;
> +}
> +
> +static int ds1374_wdt_resume(struct device *dev)
> +{
> + return 0;
> +}
> +#endif
> +
Those functions are quite pointless.
> +static SIMPLE_DEV_PM_OPS(ds1374_wdt_pm, ds1374_wdt_suspend, ds1374_wdt_resume);
> +
> +static struct platform_driver ds1374_wdt_driver = {
> + .probe = ds1374_wdt_probe,
> + .remove = ds1374_wdt_remove,
> + .driver = {
> + .name = "ds1374-wdt",
> + .pm = &ds1374_wdt_pm,
> + },
> +};
> +module_platform_driver(ds1374_wdt_driver);
> +
> +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> +MODULE_DESCRIPTION("Maxim/Dallas DS1374 WDT Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:ds1374-wdt");
> diff --git a/include/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
> new file mode 100644
> index 0000000..b33cd5e
> --- /dev/null
> +++ b/include/dt-bindings/mfd/ds1374.h
> @@ -0,0 +1,17 @@
> +/*
> + * This header provides macros for Maxim/Dallas DS1374 DT bindings
> + *
> + * Copyright (C) 2017 National Instruments Corp
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + *
> + */
> +
> +#ifndef __DT_BINDINGS_MFD_DS1374_H__
> +#define __DT_BINDINGS_MFD_DS1374_H__
> +
> +#define DALLAS_MODE_RTC 0
> +#define DALLAS_MODE_ALM 1
> +#define DALLAS_MODE_WDT 2
> +
> +#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
> diff --git a/include/linux/mfd/ds1374.h b/include/linux/mfd/ds1374.h
> new file mode 100644
> index 0000000..7b697f8
> --- /dev/null
> +++ b/include/linux/mfd/ds1374.h
> @@ -0,0 +1,59 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Multi Function Device for Dallas/Maxim DS1374 RTC/WDT
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef MFD_DS1374_H
> +#define MFD_DS1374_H
> +
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +
> +enum ds1374_mode {
> + DS1374_MODE_RTC_ONLY,
> + DS1374_MODE_RTC_ALM,
> + DS1374_MODE_RTC_WDT,
> +};
> +
> +/* Register definitions to for all subdrivers
> + */
> +#define DS1374_REG_TOD0 0x00 /* Time of Day */
> +#define DS1374_REG_TOD1 0x01
> +#define DS1374_REG_TOD2 0x02
> +#define DS1374_REG_TOD3 0x03
> +#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
> +#define DS1374_REG_WDALM1 0x05
> +#define DS1374_REG_WDALM2 0x06
> +#define DS1374_REG_CR 0x07 /* Control */
> +#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
> +#define DS1374_REG_CR_WDSTR 0x08 /* 1=Reset on INT, 0=Rreset on RST */
> +#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
> +#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
> +#define DS1374_REG_SR 0x08 /* Status */
> +#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
> +#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
> +#define DS1374_REG_TCR 0x09 /* Trickle Charge */
> +
> +struct ds1374 {
> + struct i2c_client *client;
> + struct regmap *regmap;
> + int irq;
> + enum ds1374_mode mode;
> + bool remapped_reset;
> +};
> +
> +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes);
> +
> +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes);
> +
> +#endif /* MFD_DS1374_H */
>
^ permalink raw reply
* Re: [rtc-linux] Re: [PATCH] RTC: Add functionality to read/write rtc scratch registers
From: Alexandre Belloni @ 2017-07-13 20:43 UTC (permalink / raw)
To: Keerthy; +Cc: linux-rtc, linux-kernel, linux-omap, Russ Dill
In-Reply-To: <20170504223827.l56sbaozuarq5xwi@piout.net>
Hi,
On 05/05/2017 at 00:38:27 +0200, Alexandre Belloni wrote:
> Hi,
>
> On 03/05/2017 at 11:39:34 +0530, Keerthy wrote:
> > On Tuesday 18 April 2017 10:50 AM, Keerthy wrote:
> > > From: Russ Dill <Russ.Dill@ti.com>
> > >
> > > Many RTCs provide scratch registers that are maintained so long as the RTC
> > > has power. Provide a generic method to access these registers.
> > >
> >
> > A gentle ping on this
> >
>
> Yeah, I apologize for the delay (I know you already sent this 6-8 months
> ago).
> I'm not satisfied with the interface (and its name). I gave a lot of
> thought into it and I'm currently reworking part of the subsystem to
> create a similar feature. I'm planning to have it in 4.13.
>
So, the nvmem stuff is now in 4.13. If you can still wait a bit, I'll
implement it myself and test on am335x.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [rtc-linux] Re: [PATCH v3 1/4] mfd: cros_ec: Get rid of cros_ec_check_features from cros_ec_dev.
From: Gwendal Grignou @ 2017-07-13 20:33 UTC (permalink / raw)
To: Enric Balletbo i Serra
Cc: Jonathan Cameron, Lee Jones, Benson Leung, martinez.javier,
Guenter Roeck, Gwendal Grignou, Linux Kernel, linux-iio,
rtc-linux
In-Reply-To: <20170712101309.6045-2-enric.balletbo@collabora.com>
On Wed, Jul 12, 2017 at 3:13 AM, Enric Balletbo i Serra
<enric.balletbo@collabora.com> wrote:
> The cros_ec_dev driver should be used only to expose the Chrome OS Embedded
> Controller to user-space and should not be used to add MFD devices by
> calling mfd_add_devices. This patch moves this logic to the MFD cros_ec
> driver and removes the MFD bits from the character device driver. Also
> makes independent the IIO driver from the character device as also has no
> sense.
cros_ec_dev serves another purpose: it allows to represent an EC that
does not have a cros_ec structure. It happens when there are several
EC in a chromebook, and one EC is connected through another EC.
One example is Samus (Pixel 2): where we have:
(main SOC, Application Processor) AP --> (main Embedded Controller) EC
---> (Power Delivery [PD}) EC
We access to the PD EC via pass-through commands through the main EC.
Each EC has a cros_ec_dev structure, but only the main EC as a
cros_ec_device structure (I will forever regret the structure names).
Now form the AP point of view, both ECs use the same protocol. That
why the sensors and other devcies that are registered by looking at
the feature fields are registered with cros_ec_dev as their parent.
Other devices that are registered from the device tree, predating the
feature field support, are registered with cros_ec_device as their
parent.
Gwendal.
>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> .../iio/common/cros_ec_sensors/cros_ec_sensors.c | 8 -
> .../common/cros_ec_sensors/cros_ec_sensors_core.c | 8 +-
> drivers/iio/light/cros_ec_light_prox.c | 8 -
> drivers/iio/pressure/cros_ec_baro.c | 8 -
> drivers/mfd/cros_ec.c | 160 ++++++++++++++++++++
> drivers/platform/chrome/cros_ec_dev.c | 161 ---------------------
> include/linux/mfd/cros_ec.h | 6 +-
> 7 files changed, 170 insertions(+), 189 deletions(-)
>
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> index 38e8783..9b53a01 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -191,19 +191,11 @@ static const struct iio_info ec_sensors_info = {
> static int cros_ec_sensors_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> - struct cros_ec_device *ec_device;
> struct iio_dev *indio_dev;
> struct cros_ec_sensors_state *state;
> struct iio_chan_spec *channel;
> int ret, i;
>
> - if (!ec_dev || !ec_dev->ec_dev) {
> - dev_warn(&pdev->dev, "No CROS EC device found.\n");
> - return -EINVAL;
> - }
> - ec_device = ec_dev->ec_dev;
> -
> indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
> if (!indio_dev)
> return -ENOMEM;
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> index 416cae5..0cdb64a 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -41,12 +41,13 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
> {
> struct device *dev = &pdev->dev;
> struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
> - struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
> + struct cros_ec_device *ec_dev = dev_get_drvdata(pdev->dev.parent);
> struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
>
> platform_set_drvdata(pdev, indio_dev);
>
> - state->ec = ec->ec_dev;
> + state->ec = ec_dev;
> +
> state->msg = devm_kzalloc(&pdev->dev,
> max((u16)sizeof(struct ec_params_motion_sense),
> state->ec->max_response), GFP_KERNEL);
> @@ -59,7 +60,8 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>
> /* Set up the host command structure. */
> state->msg->version = 2;
> - state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
> + state->msg->command = EC_CMD_MOTION_SENSE_CMD +
> + sensor_platform->cmd_offset;
> state->msg->outsize = sizeof(struct ec_params_motion_sense);
>
> indio_dev->dev.parent = &pdev->dev;
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index 7217223..2133ddc 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -181,19 +181,11 @@ static const struct iio_info cros_ec_light_prox_info = {
> static int cros_ec_light_prox_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> - struct cros_ec_device *ec_device;
> struct iio_dev *indio_dev;
> struct cros_ec_light_prox_state *state;
> struct iio_chan_spec *channel;
> int ret;
>
> - if (!ec_dev || !ec_dev->ec_dev) {
> - dev_warn(dev, "No CROS EC device found.\n");
> - return -EINVAL;
> - }
> - ec_device = ec_dev->ec_dev;
> -
> indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
> if (!indio_dev)
> return -ENOMEM;
> diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
> index 48b2a30..dbea18b 100644
> --- a/drivers/iio/pressure/cros_ec_baro.c
> +++ b/drivers/iio/pressure/cros_ec_baro.c
> @@ -126,19 +126,11 @@ static const struct iio_info cros_ec_baro_info = {
> static int cros_ec_baro_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> - struct cros_ec_device *ec_device;
> struct iio_dev *indio_dev;
> struct cros_ec_baro_state *state;
> struct iio_chan_spec *channel;
> int ret;
>
> - if (!ec_dev || !ec_dev->ec_dev) {
> - dev_warn(dev, "No CROS EC device found.\n");
> - return -EINVAL;
> - }
> - ec_device = ec_dev->ec_dev;
> -
> indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
> if (!indio_dev)
> return -ENOMEM;
> diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
> index b0ca5a4c..75a27a6 100644
> --- a/drivers/mfd/cros_ec.c
> +++ b/drivers/mfd/cros_ec.c
> @@ -91,6 +91,160 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
> return cros_ec_cmd_xfer(ec_dev, &buf.msg);
> }
>
> +static int cros_ec_check_features(struct cros_ec_device *ec_dev, int feature)
> +{
> + struct cros_ec_command *msg;
> + int ret;
> +
> + if (ec_dev->features[0] == -1U && ec_dev->features[1] == -1U) {
> + /* features bitmap not read yet */
> +
> + msg = kmalloc(sizeof(*msg) + sizeof(ec_dev->features),
> + GFP_KERNEL);
> + if (!msg)
> + return -ENOMEM;
> +
> + msg->version = 0;
> + msg->command = EC_CMD_GET_FEATURES + ec_p.cmd_offset;
> + msg->insize = sizeof(ec_dev->features);
> + msg->outsize = 0;
> +
> + ret = cros_ec_cmd_xfer(ec_dev, msg);
> + if (ret < 0 || msg->result != EC_RES_SUCCESS) {
> + dev_warn(ec_dev->dev, "cannot get EC features: %d/%d\n",
> + ret, msg->result);
> + memset(ec_dev->features, 0, sizeof(ec_dev->features));
> + }
> +
> + memcpy(ec_dev->features, msg->data, sizeof(ec_dev->features));
> +
> + dev_dbg(ec_dev->dev, "EC features %08x %08x\n",
> + ec_dev->features[0], ec_dev->features[1]);
> +
> + kfree(msg);
> + }
> +
> + return ec_dev->features[feature / 32] & EC_FEATURE_MASK_0(feature);
> +}
> +
> +static void cros_ec_sensors_register(struct cros_ec_device *ec_dev)
> +{
> + /*
> + * Issue a command to get the number of sensor reported.
> + * Build an array of sensors driver and register them all.
> + */
> + int ret, i, id, sensor_num;
> + struct mfd_cell *sensor_cells;
> + struct cros_ec_sensor_platform *sensor_platforms;
> + int sensor_type[MOTIONSENSE_TYPE_MAX];
> + struct ec_params_motion_sense *params;
> + struct ec_response_motion_sense *resp;
> + struct cros_ec_command *msg;
> +
> + msg = kzalloc(sizeof(struct cros_ec_command) +
> + max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> + if (msg == NULL)
> + return;
> +
> + msg->version = 2;
> + msg->command = EC_CMD_MOTION_SENSE_CMD + ec_p.cmd_offset;
> + msg->outsize = sizeof(*params);
> + msg->insize = sizeof(*resp);
> +
> + params = (struct ec_params_motion_sense *)msg->data;
> + params->cmd = MOTIONSENSE_CMD_DUMP;
> +
> + ret = cros_ec_cmd_xfer(ec_dev, msg);
> + if (ret < 0 || msg->result != EC_RES_SUCCESS) {
> + dev_warn(ec_dev->dev, "cannot get EC sensor information: %d/%d\n",
> + ret, msg->result);
> + goto error;
> + }
> +
> + resp = (struct ec_response_motion_sense *)msg->data;
> + sensor_num = resp->dump.sensor_count;
> + /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
> + sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
> + GFP_KERNEL);
> + if (sensor_cells == NULL)
> + goto error;
> +
> + sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
> + (sensor_num + 1), GFP_KERNEL);
> + if (sensor_platforms == NULL)
> + goto error_platforms;
> +
> + memset(sensor_type, 0, sizeof(sensor_type));
> + id = 0;
> + for (i = 0; i < sensor_num; i++) {
> + params->cmd = MOTIONSENSE_CMD_INFO;
> + params->info.sensor_num = i;
> + ret = cros_ec_cmd_xfer(ec_dev, msg);
> + if (ret < 0 || msg->result != EC_RES_SUCCESS) {
> + dev_warn(ec_dev->dev, "no info for EC sensor %d : %d/%d\n",
> + i, ret, msg->result);
> + continue;
> + }
> + switch (resp->info.type) {
> + case MOTIONSENSE_TYPE_ACCEL:
> + sensor_cells[id].name = "cros-ec-accel";
> + break;
> + case MOTIONSENSE_TYPE_BARO:
> + sensor_cells[id].name = "cros-ec-baro";
> + break;
> + case MOTIONSENSE_TYPE_GYRO:
> + sensor_cells[id].name = "cros-ec-gyro";
> + break;
> + case MOTIONSENSE_TYPE_MAG:
> + sensor_cells[id].name = "cros-ec-mag";
> + break;
> + case MOTIONSENSE_TYPE_PROX:
> + sensor_cells[id].name = "cros-ec-prox";
> + break;
> + case MOTIONSENSE_TYPE_LIGHT:
> + sensor_cells[id].name = "cros-ec-light";
> + break;
> + case MOTIONSENSE_TYPE_ACTIVITY:
> + sensor_cells[id].name = "cros-ec-activity";
> + break;
> + default:
> + dev_warn(ec_dev->dev, "unknown type %d\n",
> + resp->info.type);
> + continue;
> + }
> + sensor_platforms[id].sensor_num = i;
> + sensor_platforms[id].cmd_offset = ec_p.cmd_offset;
> + sensor_cells[id].id = sensor_type[resp->info.type];
> + sensor_cells[id].platform_data = &sensor_platforms[id];
> + sensor_cells[id].pdata_size =
> + sizeof(struct cros_ec_sensor_platform);
> +
> + sensor_type[resp->info.type]++;
> + id++;
> + }
> + if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
> + sensor_platforms[id].sensor_num = sensor_num;
> +
> + sensor_cells[id].name = "cros-ec-angle";
> + sensor_cells[id].id = 0;
> + sensor_cells[id].platform_data = &sensor_platforms[id];
> + sensor_cells[id].pdata_size =
> + sizeof(struct cros_ec_sensor_platform);
> + id++;
> + }
> +
> + ret = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, sensor_cells,
> + id, NULL, 0, NULL);
> + if (ret)
> + dev_err(ec_dev->dev, "failed to add EC sensors\n");
> +
> + kfree(sensor_platforms);
> +error_platforms:
> + kfree(sensor_cells);
> +error:
> + kfree(msg);
> +}
> +
> int cros_ec_register(struct cros_ec_device *ec_dev)
> {
> struct device *dev = ec_dev->dev;
> @@ -101,6 +255,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
> ec_dev->max_request = sizeof(struct ec_params_hello);
> ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
> ec_dev->max_passthru = 0;
> + ec_dev->features[0] = -1U; /* Not cached yet */
> + ec_dev->features[1] = -1U; /* Not cached yet */
>
> ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
> if (!ec_dev->din)
> @@ -134,6 +290,10 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
> goto fail_mfd;
> }
>
> + /* Check whether this EC is a sensor hub. */
> + if (cros_ec_check_features(ec_dev, EC_FEATURE_MOTION_SENSE))
> + cros_ec_sensors_register(ec_dev);
> +
> if (ec_dev->max_passthru) {
> /*
> * Register a PD device as well on top of this device.
> diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
> index cf6c4f0..bd07df5 100644
> --- a/drivers/platform/chrome/cros_ec_dev.c
> +++ b/drivers/platform/chrome/cros_ec_dev.c
> @@ -90,41 +90,6 @@ static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
> return ret;
> }
>
> -static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
> -{
> - struct cros_ec_command *msg;
> - int ret;
> -
> - if (ec->features[0] == -1U && ec->features[1] == -1U) {
> - /* features bitmap not read yet */
> -
> - msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
> - if (!msg)
> - return -ENOMEM;
> -
> - msg->version = 0;
> - msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
> - msg->insize = sizeof(ec->features);
> - msg->outsize = 0;
> -
> - ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
> - if (ret < 0 || msg->result != EC_RES_SUCCESS) {
> - dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
> - ret, msg->result);
> - memset(ec->features, 0, sizeof(ec->features));
> - }
> -
> - memcpy(ec->features, msg->data, sizeof(ec->features));
> -
> - dev_dbg(ec->dev, "EC features %08x %08x\n",
> - ec->features[0], ec->features[1]);
> -
> - kfree(msg);
> - }
> -
> - return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
> -}
> -
> /* Device file ops */
> static int ec_device_open(struct inode *inode, struct file *filp)
> {
> @@ -268,126 +233,6 @@ static void __remove(struct device *dev)
> kfree(ec);
> }
>
> -static void cros_ec_sensors_register(struct cros_ec_dev *ec)
> -{
> - /*
> - * Issue a command to get the number of sensor reported.
> - * Build an array of sensors driver and register them all.
> - */
> - int ret, i, id, sensor_num;
> - struct mfd_cell *sensor_cells;
> - struct cros_ec_sensor_platform *sensor_platforms;
> - int sensor_type[MOTIONSENSE_TYPE_MAX];
> - struct ec_params_motion_sense *params;
> - struct ec_response_motion_sense *resp;
> - struct cros_ec_command *msg;
> -
> - msg = kzalloc(sizeof(struct cros_ec_command) +
> - max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> - if (msg == NULL)
> - return;
> -
> - msg->version = 2;
> - msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
> - msg->outsize = sizeof(*params);
> - msg->insize = sizeof(*resp);
> -
> - params = (struct ec_params_motion_sense *)msg->data;
> - params->cmd = MOTIONSENSE_CMD_DUMP;
> -
> - ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
> - if (ret < 0 || msg->result != EC_RES_SUCCESS) {
> - dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
> - ret, msg->result);
> - goto error;
> - }
> -
> - resp = (struct ec_response_motion_sense *)msg->data;
> - sensor_num = resp->dump.sensor_count;
> - /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
> - sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
> - GFP_KERNEL);
> - if (sensor_cells == NULL)
> - goto error;
> -
> - sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
> - (sensor_num + 1), GFP_KERNEL);
> - if (sensor_platforms == NULL)
> - goto error_platforms;
> -
> - memset(sensor_type, 0, sizeof(sensor_type));
> - id = 0;
> - for (i = 0; i < sensor_num; i++) {
> - params->cmd = MOTIONSENSE_CMD_INFO;
> - params->info.sensor_num = i;
> - ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
> - if (ret < 0 || msg->result != EC_RES_SUCCESS) {
> - dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
> - i, ret, msg->result);
> - continue;
> - }
> - switch (resp->info.type) {
> - case MOTIONSENSE_TYPE_ACCEL:
> - sensor_cells[id].name = "cros-ec-accel";
> - break;
> - case MOTIONSENSE_TYPE_BARO:
> - sensor_cells[id].name = "cros-ec-baro";
> - break;
> - case MOTIONSENSE_TYPE_GYRO:
> - sensor_cells[id].name = "cros-ec-gyro";
> - break;
> - case MOTIONSENSE_TYPE_MAG:
> - sensor_cells[id].name = "cros-ec-mag";
> - break;
> - case MOTIONSENSE_TYPE_PROX:
> - sensor_cells[id].name = "cros-ec-prox";
> - break;
> - case MOTIONSENSE_TYPE_LIGHT:
> - sensor_cells[id].name = "cros-ec-light";
> - break;
> - case MOTIONSENSE_TYPE_ACTIVITY:
> - sensor_cells[id].name = "cros-ec-activity";
> - break;
> - default:
> - dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
> - continue;
> - }
> - sensor_platforms[id].sensor_num = i;
> - sensor_cells[id].id = sensor_type[resp->info.type];
> - sensor_cells[id].platform_data = &sensor_platforms[id];
> - sensor_cells[id].pdata_size =
> - sizeof(struct cros_ec_sensor_platform);
> -
> - sensor_type[resp->info.type]++;
> - id++;
> - }
> - if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
> - sensor_platforms[id].sensor_num = sensor_num;
> -
> - sensor_cells[id].name = "cros-ec-angle";
> - sensor_cells[id].id = 0;
> - sensor_cells[id].platform_data = &sensor_platforms[id];
> - sensor_cells[id].pdata_size =
> - sizeof(struct cros_ec_sensor_platform);
> - id++;
> - }
> - if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> - sensor_cells[id].name = "cros-ec-ring";
> - id++;
> - }
> -
> - ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
> - NULL, 0, NULL);
> - if (ret)
> - dev_err(ec->dev, "failed to add EC sensors\n");
> -
> - kfree(sensor_platforms);
> -error_platforms:
> - kfree(sensor_cells);
> -error:
> - kfree(msg);
> -}
> -
> static int ec_device_probe(struct platform_device *pdev)
> {
> int retval = -ENOMEM;
> @@ -402,8 +247,6 @@ static int ec_device_probe(struct platform_device *pdev)
> ec->ec_dev = dev_get_drvdata(dev->parent);
> ec->dev = dev;
> ec->cmd_offset = ec_platform->cmd_offset;
> - ec->features[0] = -1U; /* Not cached yet */
> - ec->features[1] = -1U; /* Not cached yet */
> device_initialize(&ec->class_dev);
> cdev_init(&ec->cdev, &fops);
>
> @@ -432,10 +275,6 @@ static int ec_device_probe(struct platform_device *pdev)
> if (cros_ec_debugfs_init(ec))
> dev_warn(dev, "failed to create debugfs directory\n");
>
> - /* check whether this EC is a sensor hub. */
> - if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
> - cros_ec_sensors_register(ec);
> -
> /* Take control of the lightbar from the EC. */
> lb_manual_suspend_ctrl(ec, 1);
>
> diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
> index 4e887ba..a4138a5 100644
> --- a/include/linux/mfd/cros_ec.h
> +++ b/include/linux/mfd/cros_ec.h
> @@ -115,6 +115,7 @@ struct cros_ec_command {
> * @event_notifier: interrupt event notifier for transport devices.
> * @event_data: raw payload transferred with the MKBP event.
> * @event_size: size in bytes of the event data.
> + * @features: stores the EC features.
> */
> struct cros_ec_device {
>
> @@ -150,15 +151,19 @@ struct cros_ec_device {
> struct ec_response_get_next_event event_data;
> int event_size;
> u32 host_event_wake_mask;
> + u32 features[2];
> };
>
> /**
> * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information
> *
> * @sensor_num: Id of the sensor, as reported by the EC.
> + * @cmd_offset: offset to apply for each command. Set when
> + * registering a devicde behind another one.
> */
> struct cros_ec_sensor_platform {
> u8 sensor_num;
> + u16 cmd_offset;
> };
>
> /* struct cros_ec_platform - ChromeOS EC platform information
> @@ -192,7 +197,6 @@ struct cros_ec_dev {
> struct device *dev;
> struct cros_ec_debugfs *debug_info;
> u16 cmd_offset;
> - u32 features[2];
> };
>
> /**
> --
> 2.9.3
>
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* [rtc-linux] Re: [PATCH v3 3/4] rtc: cros-ec: add cros-ec-rtc driver.
From: 'Benson Leung' via rtc-linux @ 2017-07-13 20:31 UTC (permalink / raw)
To: Enric Balletbo i Serra, Jonathan Cameron, Lee Jones, bleung
Cc: martinez.javier, Guenter Roeck, Gwendal Grignou, linux-kernel,
linux-iio, rtc-linux, Stephen Barber, 'Benson Leung'
In-Reply-To: <20170712101309.6045-4-enric.balletbo@collabora.com>
[-- Attachment #1.1: Type: text/plain, Size: 1139 bytes --]
Hi Enric,
On 07/12/2017 03:13 AM, Enric Balletbo i Serra wrote:
> From: Stephen Barber <smbarber@chromium.org>
>
> On platforms with a Chrome OS EC, the EC can function as a simple RTC.
> Add a basic driver with this functionality.
>
> Signed-off-by: Stephen Barber <smbarber@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Acked-by: Benson Leung <bleung@chromium.org>
Thanks!
Benson
--
Benson Leung
Staff Software Engineer
Chrome OS Kernel
Google Inc.
bleung@google.com
Chromium OS Project
bleung@chromium.org
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* [rtc-linux] Re: [PATCH v3 2/4] mfd: cros_ec: Introduce RTC commands and events definitions.
From: 'Benson Leung' via rtc-linux @ 2017-07-13 20:15 UTC (permalink / raw)
To: Enric Balletbo i Serra, Jonathan Cameron, Lee Jones, bleung
Cc: martinez.javier, Guenter Roeck, Gwendal Grignou, linux-kernel,
linux-iio, rtc-linux, Stephen Barber
In-Reply-To: <20170712101309.6045-3-enric.balletbo@collabora.com>
[-- Attachment #1.1: Type: text/plain, Size: 1098 bytes --]
Hi Enric,
On 07/12/2017 03:13 AM, Enric Balletbo i Serra wrote:
> From: Stephen Barber <smbarber@chromium.org>
>
> The EC can function as a simple RT, this patch adds the RTC related
> definitions needed by the rtc-cros-ec driver.
>
> Signed-off-by: Stephen Barber <smbarber@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Benson Leung <bleung@chromium.org>
--
Benson Leung
Staff Software Engineer
Chrome OS Kernel
Google Inc.
bleung@google.com
Chromium OS Project
bleung@chromium.org
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* [PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device
From: Moritz Fischer @ 2017-07-13 19:54 UTC (permalink / raw)
To: lee.jones
Cc: robh+dt, mark.rutland, a.zummo, alexandre.belloni, wim, linux,
devicetree, linux-kernel, linux-watchdog, linux-rtc,
Moritz Fischer, Moritz Fischer
In-Reply-To: <1499975665-14581-1-git-send-email-mdf@kernel.org>
From: Moritz Fischer <moritz.fischer@ettus.com>
Add support for the Maxim/Dallas DS1374 RTC/WDT with trickle charger.
The device can either be configured as simple RTC, as simple RTC with
Alarm (IRQ) as well as simple RTC with watchdog timer.
Break up the old monolithic driver in drivers/rtc/rtc-ds1374.c into:
- rtc part in drivers/rtc/rtc-ds1374.c
- watchdog part under drivers/watchdog/ds1374-wdt.c
- mfd part drivers/mfd/ds1374.c
The MFD part takes care of trickle charging and mode selection,
since the usage modes of a) RTC + Alarm or b) RTC + WDT
are mutually exclusive.
Signed-off-by: Moritz Fischer <mdf@kernel.org>
---
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 1 +
drivers/mfd/ds1374.c | 260 ++++++++++++++++
drivers/rtc/rtc-ds1374.c | 639 ++++++++++-----------------------------
drivers/watchdog/Kconfig | 10 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ds1374-wdt.c | 208 +++++++++++++
include/dt-bindings/mfd/ds1374.h | 17 ++
include/linux/mfd/ds1374.h | 59 ++++
9 files changed, 722 insertions(+), 483 deletions(-)
create mode 100644 drivers/mfd/ds1374.c
create mode 100644 drivers/watchdog/ds1374-wdt.c
create mode 100644 include/dt-bindings/mfd/ds1374.h
create mode 100644 include/linux/mfd/ds1374.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3eb5c93..2dfef3c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -203,6 +203,16 @@ config MFD_CROS_EC_SPI
response time cannot be guaranteed, we support ignoring
'pre-amble' bytes before the response actually starts.
+config MFD_DS1374
+ tristate "Dallas/Maxim DS1374 RTC/WDT/ALARM (I2C)"
+ select MFD_CORE
+ depends on I2C
+ depends on REGMAP_I2C
+
+ ---help---
+ This driver supports the Dallas Maxim DS1374 multi function chip.
+ The chip combines an RTC, trickle charger, Watchdog or Alarm.
+
config MFD_ASIC3
bool "Compaq ASIC3"
depends on GPIOLIB && ARM
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index c16bf1e..b5cfcf4 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -15,6 +15,7 @@ cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o
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_DS1374) += ds1374.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
diff --git a/drivers/mfd/ds1374.c b/drivers/mfd/ds1374.c
new file mode 100644
index 0000000..a0cfa1b
--- /dev/null
+++ b/drivers/mfd/ds1374.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Dallas/Maxim DS1374 Multi Function Device Driver
+ *
+ * The trickle charger code was taken more ore less 1:1 from
+ * drivers/rtc/rtc-1390.c
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ds1374.h>
+
+#define DS1374_TRICKLE_CHARGER_ENABLE 0xa0
+#define DS1374_TRICKLE_CHARGER_ENABLE_MASK 0xe0
+
+#define DS1374_TRICKLE_CHARGER_250_OHM 0x01
+#define DS1374_TRICKLE_CHARGER_2K_OHM 0x02
+#define DS1374_TRICKLE_CHARGER_4K_OHM 0x03
+#define DS1374_TRICKLE_CHARGER_ROUT_MASK 0x03
+
+#define DS1374_TRICKLE_CHARGER_NO_DIODE 0x04
+#define DS1374_TRICKLE_CHARGER_DIODE 0x08
+#define DS1374_TRICKLE_CHARGER_DIODE_MASK 0xc
+
+static const struct regmap_range volatile_ranges[] = {
+ regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_WDALM2),
+ regmap_reg_range(DS1374_REG_SR, DS1374_REG_SR),
+};
+
+static const struct regmap_access_table ds1374_volatile_table = {
+ .yes_ranges = volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
+};
+
+static struct regmap_config ds1374_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = DS1374_REG_TCR,
+ .volatile_table = &ds1374_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static struct mfd_cell ds1374_wdt_cell = {
+ .name = "ds1374-wdt",
+};
+
+static struct mfd_cell ds1374_rtc_cell = {
+ .name = "ds1374-rtc",
+};
+
+static int ds1374_add_device(struct ds1374 *chip,
+ struct mfd_cell *cell)
+{
+ cell->platform_data = chip;
+ cell->pdata_size = sizeof(*chip);
+
+ return mfd_add_devices(&chip->client->dev, PLATFORM_DEVID_AUTO,
+ cell, 1, NULL, 0, NULL);
+}
+
+static int ds1374_trickle_of_init(struct ds1374 *ds1374)
+{
+ u32 ohms = 0;
+ u8 value;
+ struct i2c_client *client = ds1374->client;
+
+ if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms",
+ &ohms))
+ return 0;
+
+ /* Enable charger */
+ value = DS1374_TRICKLE_CHARGER_ENABLE;
+ if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
+ value |= DS1374_TRICKLE_CHARGER_NO_DIODE;
+ else
+ value |= DS1374_TRICKLE_CHARGER_DIODE;
+
+ /* Resistor select */
+ switch (ohms) {
+ case 250:
+ value |= DS1374_TRICKLE_CHARGER_250_OHM;
+ break;
+ case 2000:
+ value |= DS1374_TRICKLE_CHARGER_2K_OHM;
+ break;
+ case 4000:
+ value |= DS1374_TRICKLE_CHARGER_4K_OHM;
+ break;
+ default:
+ dev_warn(&client->dev,
+ "Unsupported ohm value %02ux in dt\n", ohms);
+ return -EINVAL;
+ }
+ dev_dbg(&client->dev, "Trickle charge value is 0x%02x\n", value);
+
+ return regmap_write(ds1374->regmap, DS1374_REG_TCR, value);
+}
+
+int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes)
+{
+ u8 buf[4];
+ int ret;
+ int i;
+
+ if (WARN_ON(nbytes > 4))
+ return -EINVAL;
+
+ ret = regmap_bulk_read(ds1374->regmap, reg, buf, nbytes);
+ if (ret) {
+ dev_err(&ds1374->client->dev,
+ "Failed to bulkread n = %d at R%d\n",
+ nbytes, reg);
+ return ret;
+ }
+
+ for (i = nbytes - 1, *time = 0; i >= 0; i--)
+ *time = (*time << 8) | buf[i];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ds1374_read_bulk);
+
+int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes)
+{
+ u8 buf[4];
+ int i;
+
+ if (nbytes > 4) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ buf[i] = time & 0xff;
+ time >>= 8;
+ }
+
+ return regmap_bulk_write(ds1374->regmap, reg, buf, nbytes);
+}
+EXPORT_SYMBOL_GPL(ds1374_write_bulk);
+
+static int ds1374_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds1374 *ds1374;
+ u32 mode;
+ int err;
+
+ ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
+ if (!ds1374)
+ return -ENOMEM;
+
+ ds1374->regmap = devm_regmap_init_i2c(client, &ds1374_regmap_config);
+ if (IS_ERR(ds1374->regmap))
+ return PTR_ERR(ds1374->regmap);
+
+ if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
+ err = of_property_read_u32(client->dev.of_node,
+ "dallas,mode", &mode);
+ if (err < 0) {
+ dev_err(&client->dev, "missing dallas,mode property\n");
+ return -EINVAL;
+ }
+
+ ds1374->remapped_reset
+ = of_property_read_bool(client->dev.of_node,
+ "dallas,remap-reset");
+
+ ds1374->mode = (enum ds1374_mode)mode;
+ } else if (IS_ENABLED(CONFIG_RTC_DRV_DS1374_WDT)) {
+ ds1374->mode = DS1374_MODE_RTC_WDT;
+ } else {
+ ds1374->mode = DS1374_MODE_RTC_ALM;
+ }
+
+ ds1374->client = client;
+ ds1374->irq = client->irq;
+ i2c_set_clientdata(client, ds1374);
+
+ /* check if we're supposed to trickle charge */
+ err = ds1374_trickle_of_init(ds1374);
+ if (err) {
+ dev_err(&client->dev, "Failed to init trickle charger!\n");
+ return err;
+ }
+
+ /* we always have a rtc */
+ err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
+ if (err)
+ return err;
+
+ /* we might have a watchdog if configured that way */
+ if (ds1374->mode == DS1374_MODE_RTC_WDT)
+ return ds1374_add_device(ds1374, &ds1374_wdt_cell);
+
+ return err;
+}
+
+static const struct i2c_device_id ds1374_id[] = {
+ { "ds1374", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds1374_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id ds1374_of_match[] = {
+ { .compatible = "dallas,ds1374" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ds1374_of_match);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int ds1374_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int ds1374_resume(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
+
+static struct i2c_driver ds1374_driver = {
+ .driver = {
+ .name = "ds1374",
+ .of_match_table = of_match_ptr(ds1374_of_match),
+ .pm = &ds1374_pm,
+ },
+ .probe = ds1374_probe,
+ .id_table = ds1374_id,
+};
+
+static int __init ds1374_init(void)
+{
+ return i2c_add_driver(&ds1374_driver);
+}
+subsys_initcall(ds1374_init);
+
+static void __exit ds1374_exit(void)
+{
+ i2c_del_driver(&ds1374_driver);
+}
+module_exit(ds1374_exit);
+
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
+MODULE_DESCRIPTION("Maxim/Dallas DS1374 MFD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 52429f0..29ce1a9 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -1,75 +1,38 @@
/*
- * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
+ * RTC driver for the Maxim/Dallas DS1374 Real-Time Clock via MFD
*
* Based on code by Randy Vinson <rvinson@mvista.com>,
* which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
*
+ * Copyright (C) 2017 National Instruments Corp
* Copyright (C) 2014 Rose Technology
* Copyright (C) 2006-2007 Freescale Semiconductor
*
+ * SPDX-License-Identifier: GPL-2.0
+ *
* 2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
-/*
- * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
- * recommened in .../Documentation/i2c/writing-clients section
- * "Sending and receiving", using SMBus level communication is preferred.
- */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
-#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/pm.h>
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-#include <linux/fs.h>
-#include <linux/ioctl.h>
-#include <linux/miscdevice.h>
-#include <linux/reboot.h>
-#include <linux/watchdog.h>
-#endif
-
-#define DS1374_REG_TOD0 0x00 /* Time of Day */
-#define DS1374_REG_TOD1 0x01
-#define DS1374_REG_TOD2 0x02
-#define DS1374_REG_TOD3 0x03
-#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
-#define DS1374_REG_WDALM1 0x05
-#define DS1374_REG_WDALM2 0x06
-#define DS1374_REG_CR 0x07 /* Control */
-#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
-#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
-#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
-#define DS1374_REG_SR 0x08 /* Status */
-#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
-#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
-#define DS1374_REG_TCR 0x09 /* Trickle Charge */
-
-static const struct i2c_device_id ds1374_id[] = {
- { "ds1374", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ds1374_id);
+#include <linux/regmap.h>
+#include <linux/mfd/ds1374.h>
+#include <linux/platform_device.h>
-#ifdef CONFIG_OF
-static const struct of_device_id ds1374_of_match[] = {
- { .compatible = "dallas,ds1374" },
- { }
-};
-MODULE_DEVICE_TABLE(of, ds1374_of_match);
-#endif
-
-struct ds1374 {
- struct i2c_client *client;
+struct ds1374_rtc {
struct rtc_device *rtc;
+ struct ds1374 *chip;
struct work_struct work;
/* The mutex protects alarm operations, and prevents a race
@@ -80,89 +43,44 @@ struct ds1374 {
int exiting;
};
-static struct i2c_driver ds1374_driver;
-
-static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
- int reg, int nbytes)
-{
- u8 buf[4];
- int ret;
- int i;
-
- if (WARN_ON(nbytes > 4))
- return -EINVAL;
-
- ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
-
- if (ret < 0)
- return ret;
- if (ret < nbytes)
- return -EIO;
-
- for (i = nbytes - 1, *time = 0; i >= 0; i--)
- *time = (*time << 8) | buf[i];
-
- return 0;
-}
-
-static int ds1374_write_rtc(struct i2c_client *client, u32 time,
- int reg, int nbytes)
-{
- u8 buf[4];
- int i;
-
- if (nbytes > 4) {
- WARN_ON(1);
- return -EINVAL;
- }
-
- for (i = 0; i < nbytes; i++) {
- buf[i] = time & 0xff;
- time >>= 8;
- }
-
- return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
-}
-
-static int ds1374_check_rtc_status(struct i2c_client *client)
+static int ds1374_check_rtc_status(struct ds1374_rtc *ds1374)
{
int ret = 0;
- int control, stat;
+ unsigned int control, stat;
- stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
- if (stat < 0)
+ ret = regmap_read(ds1374->chip->regmap, DS1374_REG_SR, &stat);
+ if (ret)
return stat;
if (stat & DS1374_REG_SR_OSF)
- dev_warn(&client->dev,
+ dev_warn(&ds1374->chip->client->dev,
"oscillator discontinuity flagged, time unreliable\n");
- stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
-
- ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
- if (ret < 0)
+ ret = regmap_update_bits(ds1374->chip->regmap, DS1374_REG_SR,
+ DS1374_REG_SR_OSF | DS1374_REG_SR_AF, 0);
+ if (ret)
return ret;
/* If the alarm is pending, clear it before requesting
* the interrupt, so an interrupt event isn't reported
* before everything is initialized.
*/
-
- control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
- if (control < 0)
- return control;
+ ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &control);
+ if (ret)
+ return ret;
control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
- return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
+ return regmap_write(ds1374->chip->regmap, DS1374_REG_CR, control);
}
static int ds1374_read_time(struct device *dev, struct rtc_time *time)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
u32 itime;
int ret;
- ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
+ ret = ds1374_read_bulk(ds1374_rtc->chip, &itime, DS1374_REG_TOD0, 4);
if (!ret)
rtc_time_to_tm(itime, time);
@@ -171,44 +89,47 @@ static int ds1374_read_time(struct device *dev, struct rtc_time *time)
static int ds1374_set_time(struct device *dev, struct rtc_time *time)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
unsigned long itime;
rtc_tm_to_time(time, &itime);
- return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
+ return ds1374_write_bulk(ds1374_rtc->chip, itime, DS1374_REG_TOD0, 4);
}
-#ifndef CONFIG_RTC_DRV_DS1374_WDT
/* The ds1374 has a decrementer for an alarm, rather than a comparator.
* If the time of day is changed, then the alarm will need to be
* reset.
*/
static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
+ struct ds1374 *ds1374 = ds1374_rtc->chip;
+
u32 now, cur_alarm;
- int cr, sr;
+ unsigned int cr, sr;
int ret = 0;
- if (client->irq <= 0)
+ if (ds1374->irq <= 0)
return -EINVAL;
- mutex_lock(&ds1374->mutex);
+ mutex_lock(&ds1374_rtc->mutex);
- cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
if (ret < 0)
goto out;
- sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+ ret = regmap_read(ds1374->regmap, DS1374_REG_SR, &sr);
if (ret < 0)
goto out;
- ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
+ ret = ds1374_read_bulk(ds1374_rtc->chip, &now, DS1374_REG_TOD0, 4);
if (ret)
goto out;
- ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
+ ret = ds1374_read_bulk(ds1374_rtc->chip, &cur_alarm,
+ DS1374_REG_WDALM0, 3);
if (ret)
goto out;
@@ -217,20 +138,21 @@ static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
alarm->pending = !!(sr & DS1374_REG_SR_AF);
out:
- mutex_unlock(&ds1374->mutex);
+ mutex_unlock(&ds1374_rtc->mutex);
return ret;
}
static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
+ struct ds1374 *ds1374 = ds1374_rtc->chip;
+
struct rtc_time now;
unsigned long new_alarm, itime;
- int cr;
int ret = 0;
- if (client->irq <= 0)
+ if (ds1374->irq <= 0)
return -EINVAL;
ret = ds1374_read_time(dev, &now);
@@ -251,468 +173,219 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
else
new_alarm -= itime;
- mutex_lock(&ds1374->mutex);
-
- ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
- if (ret < 0)
- goto out;
+ mutex_lock(&ds1374_rtc->mutex);
/* Disable any existing alarm before setting the new one
- * (or lack thereof). */
- cr &= ~DS1374_REG_CR_WACE;
-
- ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
- if (ret < 0)
- goto out;
+ * (or lack thereof).
+ */
+ ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
+ DS1374_REG_CR_WACE, 0);
- ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
+ ret = ds1374_write_bulk(ds1374_rtc->chip, new_alarm,
+ DS1374_REG_WDALM0, 3);
if (ret)
goto out;
if (alarm->enabled) {
- cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
- cr &= ~DS1374_REG_CR_WDALM;
-
- ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
+ ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
+ DS1374_REG_CR_WACE | DS1374_REG_CR_AIE
+ | DS1374_REG_CR_WDALM,
+ DS1374_REG_CR_WACE
+ | DS1374_REG_CR_AIE);
}
out:
- mutex_unlock(&ds1374->mutex);
+ mutex_unlock(&ds1374_rtc->mutex);
return ret;
}
-#endif
static irqreturn_t ds1374_irq(int irq, void *dev_id)
{
- struct i2c_client *client = dev_id;
- struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ struct ds1374_rtc *ds1374_rtc = dev_id;
disable_irq_nosync(irq);
- schedule_work(&ds1374->work);
+ schedule_work(&ds1374_rtc->work);
return IRQ_HANDLED;
}
static void ds1374_work(struct work_struct *work)
{
- struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
- struct i2c_client *client = ds1374->client;
- int stat, control;
+ struct ds1374_rtc *ds1374_rtc = container_of(work, struct ds1374_rtc,
+ work);
+ unsigned int stat;
+ int ret;
- mutex_lock(&ds1374->mutex);
+ mutex_lock(&ds1374_rtc->mutex);
- stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
- if (stat < 0)
+ ret = regmap_read(ds1374_rtc->chip->regmap, DS1374_REG_SR, &stat);
+ if (ret)
goto unlock;
if (stat & DS1374_REG_SR_AF) {
- stat &= ~DS1374_REG_SR_AF;
- i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
-
- control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
- if (control < 0)
+ regmap_update_bits(ds1374_rtc->chip->regmap, DS1374_REG_SR,
+ DS1374_REG_SR_AF, 0);
+
+ ret = regmap_update_bits(ds1374_rtc->chip->regmap,
+ DS1374_REG_CR, DS1374_REG_CR_WACE
+ | DS1374_REG_CR_AIE,
+ 0);
+ if (ret)
goto out;
- control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
- i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
-
- rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
+ rtc_update_irq(ds1374_rtc->rtc, 1, RTC_AF | RTC_IRQF);
}
out:
- if (!ds1374->exiting)
- enable_irq(client->irq);
+ if (!ds1374_rtc->exiting)
+ enable_irq(ds1374_rtc->chip->irq);
unlock:
- mutex_unlock(&ds1374->mutex);
+ mutex_unlock(&ds1374_rtc->mutex);
}
-#ifndef CONFIG_RTC_DRV_DS1374_WDT
static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374 = platform_get_drvdata(pdev);
+ unsigned int cr;
int ret;
mutex_lock(&ds1374->mutex);
- ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &cr);
if (ret < 0)
goto out;
- if (enabled) {
- ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
- ret &= ~DS1374_REG_CR_WDALM;
- } else {
- ret &= ~DS1374_REG_CR_WACE;
- }
- ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
-
+ if (enabled)
+ regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
+ DS1374_REG_CR_WACE | DS1374_REG_CR_AIE |
+ DS1374_REG_CR_WDALM, DS1374_REG_CR_WACE |
+ DS1374_REG_CR_AIE);
+ else
+ regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
+ DS1374_REG_CR_WACE, 0);
out:
mutex_unlock(&ds1374->mutex);
return ret;
}
-#endif
-static const struct rtc_class_ops ds1374_rtc_ops = {
+static const struct rtc_class_ops ds1374_rtc_alm_ops = {
.read_time = ds1374_read_time,
.set_time = ds1374_set_time,
-#ifndef CONFIG_RTC_DRV_DS1374_WDT
.read_alarm = ds1374_read_alarm,
.set_alarm = ds1374_set_alarm,
.alarm_irq_enable = ds1374_alarm_irq_enable,
-#endif
};
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-/*
- *****************************************************************************
- *
- * Watchdog Driver
- *
- *****************************************************************************
- */
-static struct i2c_client *save_client;
-/* Default margin */
-#define WD_TIMO 131762
-
-#define DRV_NAME "DS1374 Watchdog"
-
-static int wdt_margin = WD_TIMO;
-static unsigned long wdt_is_open;
-module_param(wdt_margin, int, 0);
-MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)");
-
-static const struct watchdog_info ds1374_wdt_info = {
- .identity = "DS1374 WTD",
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
-};
-
-static int ds1374_wdt_settimeout(unsigned int timeout)
-{
- int ret = -ENOIOCTLCMD;
- int cr;
-
- ret = cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
- if (ret < 0)
- goto out;
-
- /* Disable any existing watchdog/alarm before setting the new one */
- cr &= ~DS1374_REG_CR_WACE;
-
- ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
- if (ret < 0)
- goto out;
-
- /* Set new watchdog time */
- ret = ds1374_write_rtc(save_client, timeout, DS1374_REG_WDALM0, 3);
- if (ret) {
- pr_info("couldn't set new watchdog time\n");
- goto out;
- }
-
- /* Enable watchdog timer */
- cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM;
- cr &= ~DS1374_REG_CR_AIE;
-
- ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
- if (ret < 0)
- goto out;
-
- return 0;
-out:
- return ret;
-}
-
-
-/*
- * Reload the watchdog timer. (ie, pat the watchdog)
- */
-static void ds1374_wdt_ping(void)
-{
- u32 val;
- int ret = 0;
-
- ret = ds1374_read_rtc(save_client, &val, DS1374_REG_WDALM0, 3);
- if (ret)
- pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret);
-}
-
-static void ds1374_wdt_disable(void)
-{
- int ret = -ENOIOCTLCMD;
- int cr;
-
- cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
- /* Disable watchdog timer */
- cr &= ~DS1374_REG_CR_WACE;
-
- ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
-}
-
-/*
- * Watchdog device is opened, and watchdog starts running.
- */
-static int ds1374_wdt_open(struct inode *inode, struct file *file)
-{
- struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
-
- if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
- mutex_lock(&ds1374->mutex);
- if (test_and_set_bit(0, &wdt_is_open)) {
- mutex_unlock(&ds1374->mutex);
- return -EBUSY;
- }
- /*
- * Activate
- */
- wdt_is_open = 1;
- mutex_unlock(&ds1374->mutex);
- return nonseekable_open(inode, file);
- }
- return -ENODEV;
-}
-
-/*
- * Close the watchdog device.
- */
-static int ds1374_wdt_release(struct inode *inode, struct file *file)
-{
- if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
- clear_bit(0, &wdt_is_open);
-
- return 0;
-}
-
-/*
- * Pat the watchdog whenever device is written to.
- */
-static ssize_t ds1374_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- if (len) {
- ds1374_wdt_ping();
- return 1;
- }
- return 0;
-}
-
-static ssize_t ds1374_wdt_read(struct file *file, char __user *data,
- size_t len, loff_t *ppos)
-{
- return 0;
-}
-
-/*
- * Handle commands from user-space.
- */
-static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int new_margin, options;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user((struct watchdog_info __user *)arg,
- &ds1374_wdt_info, sizeof(ds1374_wdt_info)) ? -EFAULT : 0;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, (int __user *)arg);
- case WDIOC_KEEPALIVE:
- ds1374_wdt_ping();
- return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(new_margin, (int __user *)arg))
- return -EFAULT;
-
- if (new_margin < 1 || new_margin > 16777216)
- return -EINVAL;
-
- wdt_margin = new_margin;
- ds1374_wdt_settimeout(new_margin);
- ds1374_wdt_ping();
- /* fallthrough */
- case WDIOC_GETTIMEOUT:
- return put_user(wdt_margin, (int __user *)arg);
- case WDIOC_SETOPTIONS:
- if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
- return -EFAULT;
-
- if (options & WDIOS_DISABLECARD) {
- pr_info("disable watchdog\n");
- ds1374_wdt_disable();
- }
-
- if (options & WDIOS_ENABLECARD) {
- pr_info("enable watchdog\n");
- ds1374_wdt_settimeout(wdt_margin);
- ds1374_wdt_ping();
- }
-
- return -EINVAL;
- }
- return -ENOTTY;
-}
-
-static long ds1374_wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
- struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
-
- mutex_lock(&ds1374->mutex);
- ret = ds1374_wdt_ioctl(file, cmd, arg);
- mutex_unlock(&ds1374->mutex);
-
- return ret;
-}
-
-static int ds1374_wdt_notify_sys(struct notifier_block *this,
- unsigned long code, void *unused)
-{
- if (code == SYS_DOWN || code == SYS_HALT)
- /* Disable Watchdog */
- ds1374_wdt_disable();
- return NOTIFY_DONE;
-}
-
-static const struct file_operations ds1374_wdt_fops = {
- .owner = THIS_MODULE,
- .read = ds1374_wdt_read,
- .unlocked_ioctl = ds1374_wdt_unlocked_ioctl,
- .write = ds1374_wdt_write,
- .open = ds1374_wdt_open,
- .release = ds1374_wdt_release,
- .llseek = no_llseek,
-};
-
-static struct miscdevice ds1374_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &ds1374_wdt_fops,
-};
-
-static struct notifier_block ds1374_wdt_notifier = {
- .notifier_call = ds1374_wdt_notify_sys,
+static const struct rtc_class_ops ds1374_rtc_ops = {
+ .read_time = ds1374_read_time,
+ .set_time = ds1374_set_time,
};
-#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
-/*
- *****************************************************************************
- *
- * Driver Interface
- *
- *****************************************************************************
- */
-static int ds1374_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ds1374_rtc_probe(struct platform_device *pdev)
{
- struct ds1374 *ds1374;
+ struct device *dev = &pdev->dev;
+ struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
+ struct ds1374_rtc *ds1374_rtc;
int ret;
- ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
- if (!ds1374)
+ ds1374_rtc = devm_kzalloc(dev, sizeof(*ds1374_rtc), GFP_KERNEL);
+ if (!ds1374_rtc)
return -ENOMEM;
+ ds1374_rtc->chip = ds1374;
- ds1374->client = client;
- i2c_set_clientdata(client, ds1374);
+ platform_set_drvdata(pdev, ds1374_rtc);
- INIT_WORK(&ds1374->work, ds1374_work);
- mutex_init(&ds1374->mutex);
+ INIT_WORK(&ds1374_rtc->work, ds1374_work);
+ mutex_init(&ds1374_rtc->mutex);
- ret = ds1374_check_rtc_status(client);
- if (ret)
+ ret = ds1374_check_rtc_status(ds1374_rtc);
+ if (ret) {
+ dev_err(dev, "Failed to check rtc status\n");
return ret;
+ }
- if (client->irq > 0) {
- ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
- "ds1374", client);
+ /* if the mfd device indicates is configured to run with ALM
+ * try to get the IRQ
+ */
+ if (ds1374->mode == DS1374_MODE_RTC_ALM && ds1374->irq > 0) {
+ ret = devm_request_irq(dev, ds1374->irq,
+ ds1374_irq, 0, "ds1374", ds1374_rtc);
if (ret) {
- dev_err(&client->dev, "unable to request IRQ\n");
+ dev_err(dev, "unable to request IRQ\n");
return ret;
}
- device_set_wakeup_capable(&client->dev, 1);
+ device_set_wakeup_capable(dev, 1);
+ ds1374_rtc->rtc = devm_rtc_device_register(dev,
+ "ds1374-rtc",
+ &ds1374_rtc_alm_ops,
+ THIS_MODULE);
+ } else {
+ ds1374_rtc->rtc = devm_rtc_device_register(dev, "ds1374-rtc",
+ &ds1374_rtc_ops,
+ THIS_MODULE);
}
- ds1374->rtc = devm_rtc_device_register(&client->dev, client->name,
- &ds1374_rtc_ops, THIS_MODULE);
- if (IS_ERR(ds1374->rtc)) {
- dev_err(&client->dev, "unable to register the class device\n");
- return PTR_ERR(ds1374->rtc);
+ if (IS_ERR(ds1374_rtc->rtc)) {
+ dev_err(dev, "unable to register the class device\n");
+ return PTR_ERR(ds1374_rtc->rtc);
}
-
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
- save_client = client;
- ret = misc_register(&ds1374_miscdev);
- if (ret)
- return ret;
- ret = register_reboot_notifier(&ds1374_wdt_notifier);
- if (ret) {
- misc_deregister(&ds1374_miscdev);
- return ret;
- }
- ds1374_wdt_settimeout(131072);
-#endif
-
return 0;
}
-static int ds1374_remove(struct i2c_client *client)
+static int ds1374_rtc_remove(struct platform_device *pdev)
{
- struct ds1374 *ds1374 = i2c_get_clientdata(client);
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
- misc_deregister(&ds1374_miscdev);
- ds1374_miscdev.parent = NULL;
- unregister_reboot_notifier(&ds1374_wdt_notifier);
-#endif
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
- if (client->irq > 0) {
- mutex_lock(&ds1374->mutex);
- ds1374->exiting = 1;
- mutex_unlock(&ds1374->mutex);
+ if (ds1374_rtc->chip->irq > 0) {
+ mutex_lock(&ds1374_rtc->mutex);
+ ds1374_rtc->exiting = 1;
+ mutex_unlock(&ds1374_rtc->mutex);
- devm_free_irq(&client->dev, client->irq, client);
- cancel_work_sync(&ds1374->work);
+ devm_free_irq(&pdev->dev, ds1374_rtc->chip->irq,
+ ds1374_rtc);
+ cancel_work_sync(&ds1374_rtc->work);
}
return 0;
}
#ifdef CONFIG_PM_SLEEP
-static int ds1374_suspend(struct device *dev)
+static int ds1374_rtc_suspend(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
- if (client->irq > 0 && device_may_wakeup(&client->dev))
- enable_irq_wake(client->irq);
+ if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
+ enable_irq_wake(ds1374_rtc->chip->irq);
return 0;
}
-static int ds1374_resume(struct device *dev)
+static int ds1374_rtc_resume(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
- if (client->irq > 0 && device_may_wakeup(&client->dev))
- disable_irq_wake(client->irq);
+ if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
+ disable_irq_wake(ds1374_rtc->chip->irq);
return 0;
}
#endif
-static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
+static SIMPLE_DEV_PM_OPS(ds1374_rtc_pm, ds1374_rtc_suspend, ds1374_rtc_resume);
-static struct i2c_driver ds1374_driver = {
+static struct platform_driver ds1374_rtc_driver = {
.driver = {
- .name = "rtc-ds1374",
- .pm = &ds1374_pm,
+ .name = "ds1374-rtc",
+ .pm = &ds1374_rtc_pm,
},
- .probe = ds1374_probe,
- .remove = ds1374_remove,
- .id_table = ds1374_id,
+ .probe = ds1374_rtc_probe,
+ .remove = ds1374_rtc_remove,
};
-
-module_i2c_driver(ds1374_driver);
+module_platform_driver(ds1374_rtc_driver);
MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ds1374-rtc");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 52a70ee..1703611 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -120,6 +120,16 @@ config DA9062_WATCHDOG
This driver can be built as a module. The module name is da9062_wdt.
+config DS1374_WATCHDOG
+ tristate "Maxim/Dallas 1374 Watchdog"
+ depends on MFD_DS1374
+ depends on REGMAP_I2C
+ select WATCHDOG_CORE
+ help
+ Support for the watchdog in the Maxim/Dallas DS1374 MFD.
+
+ This driver can be built as a module. The module name is ds1374-wdt.
+
config GPIO_WATCHDOG
tristate "Watchdog device controlled through GPIO-line"
depends on OF_GPIO
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index a2126e2..5b3b053 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
+obj-$(CONFIG_DS1374_WATCHDOG) += ds1374-wdt.o
obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
diff --git a/drivers/watchdog/ds1374-wdt.c b/drivers/watchdog/ds1374-wdt.c
new file mode 100644
index 0000000..d221560
--- /dev/null
+++ b/drivers/watchdog/ds1374-wdt.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
+ * drivers/rtc/rtc-ds1374.c implementation
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ds1374.h>
+
+#define DS1374_WDT_RATE 4096 /* Hz */
+#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
+#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0444);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int timeout;
+module_param(timeout, int, 0444);
+MODULE_PARM_DESC(timeout, "Watchdog timeout");
+
+struct ds1374_wdt {
+ struct ds1374 *chip;
+ struct device *dev;
+ struct watchdog_device wdd;
+};
+
+static int ds1374_wdt_stop(struct watchdog_device *wdog)
+{
+ struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+ int err;
+
+ err = regmap_update_bits(ds1374_wdt->chip->regmap, DS1374_REG_CR,
+ DS1374_REG_CR_WACE, 0);
+ if (err)
+ return err;
+
+ if (ds1374_wdt->chip->remapped_reset)
+ return regmap_update_bits(ds1374_wdt->chip->regmap,
+ DS1374_REG_CR, DS1374_REG_CR_WDSTR,
+ 0);
+
+ return 0;
+}
+
+static int ds1374_wdt_ping(struct watchdog_device *wdog)
+{
+ struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+ u32 val;
+ int err;
+
+ err = ds1374_read_bulk(ds1374_wdt->chip, &val, DS1374_REG_WDALM0, 3);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int ds1374_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int t)
+{
+ struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+ struct regmap *regmap = ds1374_wdt->chip->regmap;
+ unsigned int timeout = DS1374_WDT_RATE * t;
+ u8 remapped = ds1374_wdt->chip->remapped_reset
+ ? DS1374_REG_CR_WDSTR : 0;
+ int err;
+
+ err = regmap_update_bits(regmap, DS1374_REG_CR,
+ DS1374_REG_CR_WACE | DS1374_REG_CR_AIE, 0);
+
+ err = ds1374_write_bulk(ds1374_wdt->chip, timeout,
+ DS1374_REG_WDALM0, 3);
+ if (err) {
+ dev_err(ds1374_wdt->dev, "couldn't set new watchdog time\n");
+ return err;
+ }
+
+ ds1374_wdt->wdd.timeout = t;
+
+ return regmap_update_bits(regmap, DS1374_REG_CR,
+ (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
+ DS1374_REG_CR_AIE | DS1374_REG_CR_WDSTR),
+ (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
+ DS1374_REG_CR_AIE | remapped));
+}
+
+static int ds1374_wdt_start(struct watchdog_device *wdog)
+{
+ int err;
+ struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+
+ err = ds1374_wdt_set_timeout(wdog, wdog->timeout);
+ if (err) {
+ dev_err(ds1374_wdt->dev, "%s: failed to set timeout (%d) %u\n",
+ __func__, err, wdog->timeout);
+ return err;
+ }
+
+ err = ds1374_wdt_ping(wdog);
+ if (err) {
+ dev_err(ds1374_wdt->dev, "%s: failed to ping (%d)\n", __func__,
+ err);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct watchdog_info ds1374_wdt_info = {
+ .identity = "DS1374 WTD",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+ | WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops ds1374_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ds1374_wdt_start,
+ .stop = ds1374_wdt_stop,
+ .set_timeout = ds1374_wdt_set_timeout,
+ .ping = ds1374_wdt_ping,
+};
+
+static int ds1374_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
+ struct ds1374_wdt *priv;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->chip = ds1374;
+ platform_set_drvdata(pdev, priv);
+
+ priv->wdd.info = &ds1374_wdt_info;
+ priv->wdd.ops = &ds1374_wdt_ops;
+ priv->wdd.min_timeout = DS1374_WDT_MIN_TIMEOUT;
+ priv->wdd.timeout = DS1374_WDT_DEFAULT_TIMEOUT;
+ priv->wdd.max_timeout = 0x1ffffff / DS1374_WDT_RATE;
+ priv->wdd.parent = dev->parent;
+
+ watchdog_init_timeout(&priv->wdd, timeout, dev);
+ watchdog_set_nowayout(&priv->wdd, nowayout);
+ watchdog_stop_on_reboot(&priv->wdd);
+ watchdog_set_drvdata(&priv->wdd, priv);
+
+ err = devm_watchdog_register_device(dev, &priv->wdd);
+ if (err) {
+ dev_err(dev, "Failed to register watchdog device\n");
+ return err;
+ }
+
+ dev_info(dev, "Registered DS1374 Watchdog\n");
+
+ return 0;
+}
+
+static int ds1374_wdt_remove(struct platform_device *pdev)
+{
+ struct ds1374_wdt *priv = platform_get_drvdata(pdev);
+
+ if (!nowayout)
+ ds1374_wdt_stop(&priv->wdd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ds1374_wdt_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int ds1374_wdt_resume(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ds1374_wdt_pm, ds1374_wdt_suspend, ds1374_wdt_resume);
+
+static struct platform_driver ds1374_wdt_driver = {
+ .probe = ds1374_wdt_probe,
+ .remove = ds1374_wdt_remove,
+ .driver = {
+ .name = "ds1374-wdt",
+ .pm = &ds1374_wdt_pm,
+ },
+};
+module_platform_driver(ds1374_wdt_driver);
+
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
+MODULE_DESCRIPTION("Maxim/Dallas DS1374 WDT Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ds1374-wdt");
diff --git a/include/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
new file mode 100644
index 0000000..b33cd5e
--- /dev/null
+++ b/include/dt-bindings/mfd/ds1374.h
@@ -0,0 +1,17 @@
+/*
+ * This header provides macros for Maxim/Dallas DS1374 DT bindings
+ *
+ * Copyright (C) 2017 National Instruments Corp
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ */
+
+#ifndef __DT_BINDINGS_MFD_DS1374_H__
+#define __DT_BINDINGS_MFD_DS1374_H__
+
+#define DALLAS_MODE_RTC 0
+#define DALLAS_MODE_ALM 1
+#define DALLAS_MODE_WDT 2
+
+#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
diff --git a/include/linux/mfd/ds1374.h b/include/linux/mfd/ds1374.h
new file mode 100644
index 0000000..7b697f8
--- /dev/null
+++ b/include/linux/mfd/ds1374.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Multi Function Device for Dallas/Maxim DS1374 RTC/WDT
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MFD_DS1374_H
+#define MFD_DS1374_H
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+enum ds1374_mode {
+ DS1374_MODE_RTC_ONLY,
+ DS1374_MODE_RTC_ALM,
+ DS1374_MODE_RTC_WDT,
+};
+
+/* Register definitions to for all subdrivers
+ */
+#define DS1374_REG_TOD0 0x00 /* Time of Day */
+#define DS1374_REG_TOD1 0x01
+#define DS1374_REG_TOD2 0x02
+#define DS1374_REG_TOD3 0x03
+#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
+#define DS1374_REG_WDALM1 0x05
+#define DS1374_REG_WDALM2 0x06
+#define DS1374_REG_CR 0x07 /* Control */
+#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
+#define DS1374_REG_CR_WDSTR 0x08 /* 1=Reset on INT, 0=Rreset on RST */
+#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
+#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
+#define DS1374_REG_SR 0x08 /* Status */
+#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
+#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
+#define DS1374_REG_TCR 0x09 /* Trickle Charge */
+
+struct ds1374 {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ int irq;
+ enum ds1374_mode mode;
+ bool remapped_reset;
+};
+
+int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes);
+
+int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes);
+
+#endif /* MFD_DS1374_H */
--
2.7.4
^ permalink raw reply related
* [PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding
From: Moritz Fischer @ 2017-07-13 19:54 UTC (permalink / raw)
To: lee.jones
Cc: robh+dt, mark.rutland, a.zummo, alexandre.belloni, wim, linux,
devicetree, linux-kernel, linux-watchdog, linux-rtc,
Moritz Fischer
This adds a binding for the Maxim/Dallas DS1374 MFD.
Signed-off-by: Moritz Fischer <mdf@kernel.org>
---
Changes from RFC:
- dallas,ds1374-mode -> dallas,mode
- Clarified examples
- dallas,remap-reset property
On second thoughts the solution for the remapping found in the
discussion did not seem to be a good solution, since the INT
pin indicating watchdog reset does not necessarily have to be
connected to an interrupt line on the machine, it could equally
well be hooked up to a PMIC reset input or similar.
In that case using the presence of an 'interrupt' phandle is not
a good choice.
Cheers,
Moritz
---
Documentation/devicetree/bindings/mfd/ds1374.txt | 88 ++++++++++++++++++++++
.../devicetree/bindings/trivial-devices.txt | 1 -
drivers/rtc/Kconfig | 2 +
3 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mfd/ds1374.txt
diff --git a/Documentation/devicetree/bindings/mfd/ds1374.txt b/Documentation/devicetree/bindings/mfd/ds1374.txt
new file mode 100644
index 0000000..d558b87
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ds1374.txt
@@ -0,0 +1,88 @@
+* Device tree bindings for Maxim/Dallas DS1374 Multi Function Device (MFD)
+
+The Maxim/Dallas DS1374 is a multi function device that combines rtc,
+watchdog or alarm, as well as trickle charger.
+
+The DS1374 is connected via I2C.
+
+Required properties:
+- compatible: "dallas,ds1374"
+- reg: I2C slave address
+- dallas,mode: Should be one of the following values:
+ <0> for RTC
+ <1> for RTC + Alarm (Interrupt)
+ <2> for RTC + Watchdog
+
+For convenience there's an include file with constants avaialable
+at include/dt-bindings/mfd/ds1374.h
+
+Required child:
+A single available child device of type matching the "dallas,mode"
+property.
+
+See ../watchdog/* for generic watchdog bindings.
+
+Optional properties (real time clock):
+- interrupt: phandle to interrupt cell for the rtc's alarm feature
+
+See ../rtc/* for generic rtc bindings.
+
+Optional properties (trickle-charger):
+- dallas,trickle-resistor-ohms : Selected resistor for trickle charger
+ Values usable for ds1374 are 250, 2000, 4000
+ Should be given if trickle charger should be enabled
+- dallas,trickle-diode-disable : Do not use internal trickle charger diode
+ Should be given if internal trickle charger diode should be disabled
+
+Optional properties (watchdog):
+- dallas,remap-reset : If present, the INT pin of the device will toggle on
+ watchdog timeout instead of the RST pin. This effectively muxes the
+ INT pin to the RST pin.
+
+Example for just rtc:
+
+i2c@12ca0000 {
+ rtc@68 {
+ compatible = "ds1374";
+ reg = <0x68>;
+ dallas,mode = <0>
+ };
+};
+
+Example for rtc with alarm mode and interrupt (and trickle charging):
+
+i2c@12ca0000 {
+ rtc@68 {
+ compatible = "ds1374";
+ reg = <0x68>;
+ interrupts = <&gpio0 62>;
+ dallas,mode = <1>
+
+ dallas,trickle-resistor-ohms = <250>;
+ dallas,trickle-diode-disable;
+ };
+};
+
+Example for rtc with watchdog and reset on timeout, with watchdog reset
+remapped to the INT pin (also no trickle charging):
+
+i2c@12ca0000 {
+ rtc@68 {
+ compatible = "ds1374";
+ reg = <0x68>;
+ dallas,mode = <2>
+ dallas,remap-reset;
+ reset-on-timeout;
+ };
+};
+
+Example for rtc with watchdog and reset on timeout, with reset not
+remapped to the INT pin (also no trickle charging):
+
+i2c@12ca0000 {
+ rtc@68 {
+ compatible = "ds1374";
+ reg = <0x68>;
+ dallas,mode = <2>
+ };
+};
diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt
index ad10fbe..42d99d1 100644
--- a/Documentation/devicetree/bindings/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/trivial-devices.txt
@@ -29,7 +29,6 @@ cirrus,cs42l51 Cirrus Logic CS42L51 audio codec
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
dallas,ds1338 I2C RTC with 56-Byte NV RAM
dallas,ds1340 I2C RTC with Trickle Charger
-dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
dallas,ds1631 High-Precision Digital Thermometer
dallas,ds1682 Total-Elapsed-Time Recorder with Alarm
dallas,ds1775 Tiny Digital Thermometer and Thermostat
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ee1b0e9..20dde9b 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -250,6 +250,8 @@ config RTC_DRV_DS1307_CENTURY
config RTC_DRV_DS1374
tristate "Dallas/Maxim DS1374"
+ depends on MFD_DS1374
+ depends on REGMAP_I2C
help
If you say yes here you get support for Dallas Semiconductor
DS1374 real-time clock chips. If an interrupt is associated
--
2.7.4
^ permalink raw reply related
* Re: [GIT PULL] RTC for 4.13
From: Alexandre Belloni @ 2017-07-13 18:18 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-rtc, linux-kernel
In-Reply-To: <20170713092239.r6cndjmnsrb35c34@piout.net>
Me again...
On 13/07/2017 at 11:22:39 +0200, Alexandre Belloni wrote:
> On 13/07/2017 at 11:01:51 +0200, Alexandre Belloni wrote:
> > Hi Linus,
> >
> > Here is the pull-request for the RTC subsystem for 4.13.
> >
>
> And I forgot to add that you will see a small Add/Add merge conflict in
> tools/testing/selftests/timers/Makefile which is easy to solve.
>
And its not actually add/add but both patches are adding a target on a
different line.
Sorry about the miscommunication, I'll get back to wiping baby poop.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [GIT PULL] RTC for 4.13
From: Alexandre Belloni @ 2017-07-13 9:22 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-rtc, linux-kernel
In-Reply-To: <20170713090151.47rrpc62dxcqwvtz@piout.net>
On 13/07/2017 at 11:01:51 +0200, Alexandre Belloni wrote:
> Hi Linus,
>
> Here is the pull-request for the RTC subsystem for 4.13.
>
And I forgot to add that you will see a small Add/Add merge conflict in
tools/testing/selftests/timers/Makefile which is easy to solve.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [GIT PULL] RTC for 4.13
From: Alexandre Belloni @ 2017-07-13 9:01 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-rtc, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 7088 bytes --]
Hi Linus,
Here is the pull-request for the RTC subsystem for 4.13.
The following changes since commit 2ea659a9ef488125eb46da6eb571de5eae5c43f6:
Linux 4.12-rc1 (2017-05-13 13:19:49 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git tags/rtc-4.13
for you to fetch changes up to 40bf6a35483ee25271ce2a90d8976cf1409a033a:
rtc: Remove wrong deprecation comment (2017-07-12 23:11:23 +0200)
----------------------------------------------------------------
RTC for 4.13
Subsystem:
- expose non volatile RAM using nvmem instead of open coding in many
drivers. Unfortunately, this option has to be enabled by default to not
break existing users.
- rtctest can now test for cutoff dates, showing when an RTC will start
failing to properly save time and date.
- new RTC registration functions to remove race conditions in drivers
Newly supported RTCs:
- Broadcom STB wake-timer
- Epson RX8130CE
- Maxim IC DS1308
- STMicroelectronics STM32H7
Drivers:
- ds1307: use regmap, use nvmem, more cleanups
- ds3232: temperature reading support
- gemini: renamed to ftrtc010
- m41t80: use CCF to expose the clock
- rv8803: use nvmem
- s3c: many cleanups
- st-lpc: fix y2106 bug
----------------------------------------------------------------
Alexandre Belloni (17):
rtc: sysfs: make name uniform
rtc: dev: remove rtc->name from debug message
rtc: pcf8563: avoid using rtc->name
rtc: ds1307: avoid using rtc-name
rtc: remove rtc_device.name
rtc: class separate device allocation from registration
rtc: class separate id allocation from registration
rtc: introduce new registration method
rtc: at91rm9200: remove race condition
rtc: add generic nvmem support
rtc: rv8803: switch to rtc_register_device
rtc: rv8803: use generic nvmem support
rtc: rv8803: remove rv8803_remove
rtc: ds1307: switch to rtc_register_device
rtc: ds1307: use generic nvmem
rtc: ds1307: remove ds1307_remove
rtc: Remove wrong deprecation comment
Amelie Delaunay (2):
dt-bindings: rtc: stm32: add support for STM32H7
rtc: stm32: add STM32H7 RTC support
Arnd Bergmann (1):
nvmem: include linux/err.h from header
Benjamin Gaignard (3):
tools: timer: add rtctest_setdate
rtc: rtctest: add check for problematic dates
rtc: st-lpc: make it robust against y2038/2106 bug
Brian Norris (1):
rtc: brcmstb-waketimer: Add Broadcom STB wake-timer
Dan Carpenter (1):
rtc: rtc-nuc900: fix loop timeout test
David Lowe (1):
rtc: rtc-ds1307: enable support for mcp794xx as a wakeup source without IRQ
Diaz de Grenu, Jose (1):
rtc: mxc: remove unused variable
Florian Fainelli (1):
dt-bindings: Document the Broadcom STB wake-up timer node
Gary Bisson (4):
rtc: m41t80: fix SQWE override when setting an alarm
rtc: m41t80: fix SQW dividers override when setting a date
rtc: m41t80: remove sqw sysfs entry
rtc: m41t80: add clock provider support
Heiner Kallweit (3):
rtc: ds1307: convert driver to regmap
rtc: ds1307: use regmap_update_bits where applicable
rtc: ds1307: factor out century bit handling
Kirill Esipov (1):
rtc: ds3232: add temperature support
Krzysztof Kozlowski (6):
rtc: s3c: Jump to central exit point on getting src clock error
rtc: s3c: Minor white-space cleanups
rtc: s3c: Drop unneeded cast to void pointer
rtc: s3c: Do not remove const from rodata memory
rtc: s3c: Handle clock prepare failures in probe
rtc: s3c: Handle clock enable failures
Linus Walleij (3):
rtc: gemini: Add optional clock handling
rtc: gemini: Augment DT bindings for Faraday
rtc: gemini/ftrtc010: rename driver and symbols
Marek Vasut (1):
rtc: ds1307: Add support for Epson RX8130CE
Mauro Carvalho Chehab (1):
rtc.txt: standardize document format
Sean Nyekjaer (1):
rtc: ds1307: add ds1308 variant
Vaibhav Jain (3):
rtc: opal: Handle disabled TPO in opal_get_tpo_time()
rtc: interface: Validate alarm-time before handling rollover
rtc: opal: Implement rtc_class_ops.alarm_irq_enable callback
.../bindings/rtc/brcm,brcmstb-waketimer.txt | 22 +
.../devicetree/bindings/rtc/cortina,gemini.txt | 14 -
.../devicetree/bindings/rtc/faraday,ftrtc010.txt | 28 +
.../devicetree/bindings/rtc/st,stm32-rtc.txt | 32 +-
Documentation/rtc.txt | 46 +-
MAINTAINERS | 2 +-
drivers/rtc/Kconfig | 37 +-
drivers/rtc/Makefile | 4 +-
drivers/rtc/class.c | 202 +++--
drivers/rtc/interface.c | 9 +-
drivers/rtc/nvmem.c | 113 +++
drivers/rtc/rtc-at91rm9200.c | 14 +-
drivers/rtc/rtc-brcmstb-waketimer.c | 330 +++++++
drivers/rtc/rtc-core.h | 8 +
drivers/rtc/rtc-dev.c | 2 +-
drivers/rtc/rtc-ds1307.c | 957 ++++++++++-----------
drivers/rtc/rtc-ds3232.c | 119 +++
drivers/rtc/{rtc-gemini.c => rtc-ftrtc010.c} | 119 ++-
drivers/rtc/rtc-m41t80.c | 251 ++++--
drivers/rtc/rtc-mxc.c | 11 -
drivers/rtc/rtc-nuc900.c | 2 +-
drivers/rtc/rtc-opal.c | 32 +-
drivers/rtc/rtc-pcf8563.c | 2 +-
drivers/rtc/rtc-rv8803.c | 72 +-
drivers/rtc/rtc-s3c.c | 147 ++--
drivers/rtc/rtc-st-lpc.c | 19 +-
drivers/rtc/rtc-stm32.c | 82 +-
drivers/rtc/rtc-sysfs.c | 3 +-
include/linux/nvmem-provider.h | 3 +
include/linux/rtc.h | 21 +-
tools/testing/selftests/timers/Makefile | 2 +-
tools/testing/selftests/timers/rtctest.c | 128 ++-
tools/testing/selftests/timers/rtctest_setdate.c | 86 ++
33 files changed, 2027 insertions(+), 892 deletions(-)
create mode 100644 Documentation/devicetree/bindings/rtc/brcm,brcmstb-waketimer.txt
delete mode 100644 Documentation/devicetree/bindings/rtc/cortina,gemini.txt
create mode 100644 Documentation/devicetree/bindings/rtc/faraday,ftrtc010.txt
create mode 100644 drivers/rtc/nvmem.c
create mode 100644 drivers/rtc/rtc-brcmstb-waketimer.c
rename drivers/rtc/{rtc-gemini.c => rtc-ftrtc010.c} (50%)
create mode 100644 tools/testing/selftests/timers/rtctest_setdate.c
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [rtc-linux] Re: [PATCH v2 11/22] rtc: gemini: stop using rtc deprecated functions
From: 'Hans Ulli Kroll' via rtc-linux @ 2017-07-12 17:20 UTC (permalink / raw)
To: Benjamin Gaignard
Cc: linaro-kernel, Hans Ulli Kroll, Alessandro Zummo,
Alexandre Belloni, rtc-linux, linux-kernel, linux-arm-kernel
In-Reply-To: <1499846682-14093-12-git-send-email-benjamin.gaignard@linaro.org>
Hi Benjamin,
On Wed, 12 Jul 2017, Benjamin Gaignard wrote:
> @@ -93,12 +93,12 @@ static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm)
> {
> struct ftrtc010_rtc *rtc = dev_get_drvdata(dev);
> unsigned int sec, min, hour, day;
> - unsigned long offset, time;
> + unsigned long long offset, time;
>
> if (tm->tm_year >= 2148) /* EPOCH Year + 179 */
> return -EINVAL;
>
> - rtc_tm_to_time(tm, &time);
> + time = rtc_tm_to_time64(tm);
>
> sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND);
> min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE);
later on this will do
writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD);
you are writing some 64bit value in a 32bit register ...
NACK here
Greetings from Cologne
Hans Ulli Kroll
> 1.9.1
>
>
^ permalink raw reply
* [PATCH] rtc: m41t80: constify attribute_group structures.
From: Arvind Yadav @ 2017-07-12 11:54 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: linux-rtc, linux-kernel
attribute_groups are not supposed to change at runtime. All functions
working with attribute_groups provided by <linux/sysfs.h> work
with const attribute_group. So mark the non-const structs as const.
File size before:
text data bss dec hex filename
4162 544 0 4706 1262 drivers/rtc/rtc-m41t80.o
File size After adding 'const':
text data bss dec hex filename
4226 480 0 4706 1262 drivers/rtc/rtc-m41t80.o
Signed-off-by: Arvind Yadav <arvind.yadav.cs@gmail.com>
---
drivers/rtc/rtc-m41t80.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 5ec4653..c15a19e 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -524,7 +524,7 @@ static ssize_t sqwfreq_store(struct device *dev,
NULL,
};
-static struct attribute_group attr_group = {
+static const struct attribute_group attr_group = {
.attrs = attrs,
};
--
1.9.1
^ permalink raw reply related
* [PATCH 3/3] rtc: sun6i: Remove unneeded initalization of ret in sun6i_rtc_setalarm()
From: Alexey Klimov @ 2017-07-12 10:59 UTC (permalink / raw)
To: linux-rtc
Cc: a.zummo, linux-kernel, alexandre.belloni, alexey.klimov,
maxime.ripard, wens, robh
In-Reply-To: <1499857190-30849-1-git-send-email-alexey.klimov@arm.com>
Signed-off-by: Alexey Klimov <alexey.klimov@arm.com>
---
drivers/rtc/rtc-sun6i.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index 77bc4d3..1886b85 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -362,7 +362,7 @@ static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
unsigned long time_now = 0;
unsigned long time_set = 0;
unsigned long time_gap = 0;
- int ret = 0;
+ int ret;
ret = sun6i_rtc_gettime(dev, &tm_now);
if (ret < 0) {
--
1.9.1
^ permalink raw reply related
* [PATCH 2/3] rtc: sun6i: fix memleaks and add error-path in sun6i_rtc_clk_init()
From: Alexey Klimov @ 2017-07-12 10:59 UTC (permalink / raw)
To: linux-rtc
Cc: a.zummo, linux-kernel, alexandre.belloni, alexey.klimov,
maxime.ripard, wens, robh
In-Reply-To: <1499857190-30849-1-git-send-email-alexey.klimov@arm.com>
The memory allocated for rtc and clk_data will never be freed in
sun6i_rtc_clk_init() in case of error and return. This patch adds
required error path with memory freeing.
Fixes: 847b8bf62eb4 ("rtc: sun6i: Expose the 32kHz oscillator")
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Rob Herring <robh@kernel.org>
Signed-off-by: Alexey Klimov <alexey.klimov@arm.com>
---
drivers/rtc/rtc-sun6i.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index 7e7da60..77bc4d3 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -197,14 +197,14 @@ static void __init sun6i_rtc_clk_init(struct device_node *node)
clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws),
GFP_KERNEL);
if (!clk_data)
- return;
+ goto out_rtc_free;
spin_lock_init(&rtc->lock);
rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node));
if (IS_ERR(rtc->base)) {
pr_crit("Can't map RTC registers");
- return;
+ goto out_clk_data_free;
}
/* Switch to the external, more precise, oscillator */
@@ -216,7 +216,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node)
/* Deal with old DTs */
if (!of_get_property(node, "clocks", NULL))
- return;
+ goto out_clk_data_free;
rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL,
"rtc-int-osc",
@@ -225,7 +225,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node)
300000000);
if (IS_ERR(rtc->int_osc)) {
pr_crit("Couldn't register the internal oscillator\n");
- return;
+ goto out_clk_data_free;
}
parents[0] = clk_hw_get_name(rtc->int_osc);
@@ -240,12 +240,19 @@ static void __init sun6i_rtc_clk_init(struct device_node *node)
rtc->losc = clk_register(NULL, &rtc->hw);
if (IS_ERR(rtc->losc)) {
pr_crit("Couldn't register the LOSC clock\n");
- return;
+ goto out_clk_data_free;
}
clk_data->num = 1;
clk_data->hws[0] = &rtc->hw;
of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+
+ return;
+
+out_clk_data_free:
+ kfree(clk_data);
+out_rtc_free:
+ kfree(rtc);
}
CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc",
sun6i_rtc_clk_init);
--
1.9.1
^ permalink raw reply related
* [PATCH 1/3] rtc: sun6i: Remove double init of spinlock in sun6i_rtc_clk_init()
From: Alexey Klimov @ 2017-07-12 10:59 UTC (permalink / raw)
To: linux-rtc
Cc: a.zummo, linux-kernel, alexandre.belloni, alexey.klimov,
maxime.ripard, wens, robh
Fixes: 847b8bf62eb4 ("rtc: sun6i: Expose the 32kHz oscillator")
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Rob Herring <robh@kernel.org>
Signed-off-by: Alexey Klimov <alexey.klimov@arm.com>
---
drivers/rtc/rtc-sun6i.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index 39cbc12..7e7da60 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -193,12 +193,12 @@ static void __init sun6i_rtc_clk_init(struct device_node *node)
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return;
- spin_lock_init(&rtc->lock);
clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws),
GFP_KERNEL);
if (!clk_data)
return;
+
spin_lock_init(&rtc->lock);
rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node));
--
1.9.1
^ permalink raw reply related
* [rtc-linux] [PATCH v3 4/4] mfd: cros_ec: add RTC as mfd subdevice
From: Enric Balletbo i Serra @ 2017-07-12 10:13 UTC (permalink / raw)
To: Jonathan Cameron, Lee Jones, bleung
Cc: martinez.javier, Guenter Roeck, Gwendal Grignou, linux-kernel,
linux-iio, rtc-linux, Stephen Barber
In-Reply-To: <20170712101309.6045-1-enric.balletbo@collabora.com>
From: Stephen Barber <smbarber@chromium.org>
If the EC supports RTC host commands, expose an RTC device.
Signed-off-by: Stephen Barber <smbarber@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
drivers/mfd/cros_ec.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index 75a27a6..ff972fb 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -51,6 +51,10 @@ static const struct mfd_cell ec_pd_cell = {
.pdata_size = sizeof(pd_p),
};
+static const struct mfd_cell ec_rtc_cell = {
+ .name = "cros-ec-rtc",
+};
+
static irqreturn_t ec_irq_thread(int irq, void *data)
{
struct cros_ec_device *ec_dev = data;
@@ -245,6 +249,16 @@ static void cros_ec_sensors_register(struct cros_ec_device *ec_dev)
kfree(msg);
}
+static void cros_ec_rtc_register(struct cros_ec_device *ec_dev)
+{
+ int ret;
+
+ ret = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_rtc_cell,
+ 1, NULL, 0, NULL);
+ if (ret)
+ dev_err(ec_dev->dev, "failed to add EC RTC\n");
+}
+
int cros_ec_register(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;
@@ -294,6 +308,10 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
if (cros_ec_check_features(ec_dev, EC_FEATURE_MOTION_SENSE))
cros_ec_sensors_register(ec_dev);
+ /* Check whether this EC has RTC support */
+ if (cros_ec_check_features(ec_dev, EC_FEATURE_RTC))
+ cros_ec_rtc_register(ec_dev);
+
if (ec_dev->max_passthru) {
/*
* Register a PD device as well on top of this device.
--
2.9.3
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply related
* [rtc-linux] [PATCH v3 3/4] rtc: cros-ec: add cros-ec-rtc driver.
From: Enric Balletbo i Serra @ 2017-07-12 10:13 UTC (permalink / raw)
To: Jonathan Cameron, Lee Jones, bleung
Cc: martinez.javier, Guenter Roeck, Gwendal Grignou, linux-kernel,
linux-iio, rtc-linux, Stephen Barber
In-Reply-To: <20170712101309.6045-1-enric.balletbo@collabora.com>
From: Stephen Barber <smbarber@chromium.org>
On platforms with a Chrome OS EC, the EC can function as a simple RTC.
Add a basic driver with this functionality.
Signed-off-by: Stephen Barber <smbarber@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-cros-ec.c | 412 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 423 insertions(+)
create mode 100644 drivers/rtc/rtc-cros-ec.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8d3b957..c255f27 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1204,6 +1204,16 @@ config RTC_DRV_ZYNQMP
If you say yes here you get support for the RTC controller found on
Xilinx Zynq Ultrascale+ MPSoC.
+config RTC_DRV_CROS_EC
+ tristate "Chrome OS EC RTC driver"
+ depends on MFD_CROS_EC
+ help
+ If you say yes here you will get support for the
+ Chrome OS Embedded Controller's RTC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-cros-ec.
+
comment "on-CPU RTC drivers"
config RTC_DRV_ASM9260
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 13857d2..8162983 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
+obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.o
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c
new file mode 100644
index 0000000..a5c2512
--- /dev/null
+++ b/drivers/rtc/rtc-cros-ec.c
@@ -0,0 +1,412 @@
+/*
+ * RTC driver for Chrome OS Embedded Controller
+ *
+ * Copyright (c) 2017, Google, Inc
+ *
+ * Author: Stephen Barber <smbarber@chromium.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define DRV_NAME "cros-ec-rtc"
+
+/**
+ * struct cros_ec_rtc - Driver data for EC RTC
+ *
+ * @cros_ec: Pointer to EC device
+ * @rtc: Pointer to RTC device
+ * @notifier: Notifier info for responding to EC events
+ * @saved_alarm: Alarm to restore when interrupts are reenabled
+ */
+struct cros_ec_rtc {
+ struct cros_ec_device *cros_ec;
+ struct rtc_device *rtc;
+ struct notifier_block notifier;
+ u32 saved_alarm;
+};
+
+static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command,
+ u32 *response)
+{
+ int ret;
+ struct {
+ struct cros_ec_command msg;
+ struct ec_response_rtc data;
+ } __packed msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg.command = command;
+ msg.msg.insize = sizeof(msg.data);
+
+ ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
+ if (ret < 0) {
+ dev_err(cros_ec->dev,
+ "error getting %s from EC: %d\n",
+ command == EC_CMD_RTC_GET_VALUE ? "time" : "alarm",
+ ret);
+ return ret;
+ }
+
+ *response = msg.data.time;
+
+ return 0;
+}
+
+static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command,
+ u32 param)
+{
+ int ret = 0;
+ struct {
+ struct cros_ec_command msg;
+ struct ec_response_rtc data;
+ } __packed msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg.command = command;
+ msg.msg.outsize = sizeof(msg.data);
+ msg.data.time = param;
+
+ ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
+ if (ret < 0) {
+ dev_err(cros_ec->dev, "error setting %s on EC: %d\n",
+ command == EC_CMD_RTC_SET_VALUE ? "time" : "alarm",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Read the current time from the EC. */
+static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
+ struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
+ int ret;
+ u32 time;
+
+ ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, &time);
+ if (ret) {
+ dev_err(dev, "error getting time: %d\n", ret);
+ return ret;
+ }
+
+ rtc_time64_to_tm(time, tm);
+
+ return 0;
+}
+
+/* Set the current EC time. */
+static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
+ struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
+ int ret;
+ time64_t time;
+
+ time = rtc_tm_to_time64(tm);
+ if (time < 0 || time > U32_MAX)
+ return -EINVAL;
+
+ ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, (u32)time);
+ if (ret < 0) {
+ dev_err(dev, "error setting time: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Read alarm time from RTC. */
+static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
+ struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
+ int ret;
+ u32 current_time, alarm_offset;
+
+ /*
+ * The EC host command for getting the alarm is relative (i.e. 5
+ * seconds from now) whereas rtc_wkalrm is absolute. Get the current
+ * RTC time first so we can calculate the relative time.
+ */
+ ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);
+ if (ret < 0) {
+ dev_err(dev, "error getting time: %d\n", ret);
+ return ret;
+ }
+
+ ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, &alarm_offset);
+ if (ret < 0) {
+ dev_err(dev, "error getting alarm: %d\n", ret);
+ return ret;
+ }
+
+ rtc_time64_to_tm(current_time + alarm_offset, &alrm->time);
+
+ return 0;
+}
+
+/* Set the EC's RTC alarm. */
+static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
+ struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
+ int ret;
+ time64_t alarm_time;
+ u32 current_time, alarm_offset;
+
+ /*
+ * The EC host command for setting the alarm is relative
+ * (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute.
+ * Get the current RTC time first so we can calculate the
+ * relative time.
+ */
+ ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);
+ if (ret < 0) {
+ dev_err(dev, "error getting time: %d\n", ret);
+ return ret;
+ }
+
+ alarm_time = rtc_tm_to_time64(&alrm->time);
+
+ if (alarm_time < 0 || alarm_time > U32_MAX)
+ return -EINVAL;
+
+ if (!alrm->enabled) {
+ /*
+ * If the alarm is being disabled, send an alarm
+ * clear command.
+ */
+ alarm_offset = EC_RTC_ALARM_CLEAR;
+ cros_ec_rtc->saved_alarm = (u32)alarm_time;
+ } else {
+ /* Don't set an alarm in the past. */
+ if ((u32)alarm_time < current_time)
+ alarm_offset = EC_RTC_ALARM_CLEAR;
+ else
+ alarm_offset = (u32)alarm_time - current_time;
+ }
+
+ ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset);
+ if (ret < 0) {
+ dev_err(dev, "error setting alarm: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cros_ec_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
+ struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
+ int ret;
+ u32 current_time, alarm_offset, alarm_value;
+
+ ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);
+ if (ret < 0) {
+ dev_err(dev, "error getting time: %d\n", ret);
+ return ret;
+ }
+
+ if (enabled) {
+ /* Restore saved alarm if it's still in the future. */
+ if (cros_ec_rtc->saved_alarm < current_time)
+ alarm_offset = EC_RTC_ALARM_CLEAR;
+ else
+ alarm_offset = cros_ec_rtc->saved_alarm - current_time;
+
+ ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM,
+ alarm_offset);
+ if (ret < 0) {
+ dev_err(dev, "error restoring alarm: %d\n", ret);
+ return ret;
+ }
+ } else {
+ /* Disable alarm, saving the old alarm value. */
+ ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM,
+ &alarm_offset);
+ if (ret < 0) {
+ dev_err(dev, "error saving alarm: %d\n", ret);
+ return ret;
+ }
+
+ alarm_value = current_time + alarm_offset;
+
+ /*
+ * If the current EC alarm is already past, we don't want
+ * to set an alarm when we go through the alarm irq enable
+ * path.
+ */
+ if (alarm_value < current_time)
+ cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR;
+ else
+ cros_ec_rtc->saved_alarm = alarm_value;
+
+ alarm_offset = EC_RTC_ALARM_CLEAR;
+ ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM,
+ alarm_offset);
+ if (ret < 0) {
+ dev_err(dev, "error disabling alarm: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int cros_ec_rtc_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend,
+ void *_notify)
+{
+ struct cros_ec_rtc *cros_ec_rtc;
+ struct rtc_device *rtc;
+ struct cros_ec_device *cros_ec;
+ u32 host_event;
+
+ cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier);
+ rtc = cros_ec_rtc->rtc;
+ cros_ec = cros_ec_rtc->cros_ec;
+
+ host_event = cros_ec_get_host_event(cros_ec);
+ if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) {
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+ return NOTIFY_OK;
+ } else {
+ return NOTIFY_DONE;
+ }
+}
+
+static const struct rtc_class_ops cros_ec_rtc_ops = {
+ .read_time = cros_ec_rtc_read_time,
+ .set_time = cros_ec_rtc_set_time,
+ .read_alarm = cros_ec_rtc_read_alarm,
+ .set_alarm = cros_ec_rtc_set_alarm,
+ .alarm_irq_enable = cros_ec_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(cros_ec_rtc->cros_ec->irq);
+
+ return 0;
+}
+
+static int cros_ec_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(cros_ec_rtc->cros_ec->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend,
+ cros_ec_rtc_resume);
+
+static int cros_ec_rtc_probe(struct platform_device *pdev)
+{
+ struct cros_ec_device *cros_ec_dev = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_rtc *cros_ec_rtc;
+ struct rtc_time tm;
+ int ret;
+
+ cros_ec_rtc = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_rtc),
+ GFP_KERNEL);
+ if (!cros_ec_rtc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cros_ec_rtc);
+ cros_ec_rtc->cros_ec = cros_ec_dev;
+
+ /* Get initial time */
+ ret = cros_ec_rtc_read_time(&pdev->dev, &tm);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read RTC time\n");
+ return ret;
+ }
+
+ ret = device_init_wakeup(&pdev->dev, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize wakeup\n");
+ return ret;
+ }
+
+ cros_ec_rtc->rtc = devm_rtc_device_register(&pdev->dev, DRV_NAME,
+ &cros_ec_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(cros_ec_rtc->rtc)) {
+ ret = PTR_ERR(cros_ec_rtc->rtc);
+ dev_err(&pdev->dev, "failed to register rtc device\n");
+ return ret;
+ }
+
+ /* Get RTC events from the EC. */
+ cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event;
+ ret = blocking_notifier_chain_register(&cros_ec_dev->event_notifier,
+ &cros_ec_rtc->notifier);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register notifier\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cros_ec_rtc_remove(struct platform_device *pdev)
+{
+ struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = blocking_notifier_chain_unregister(
+ &cros_ec_rtc->cros_ec->event_notifier,
+ &cros_ec_rtc->notifier);
+ if (ret) {
+ dev_err(dev, "failed to unregister notifier\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver cros_ec_rtc_driver = {
+ .probe = cros_ec_rtc_probe,
+ .remove = cros_ec_rtc_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &cros_ec_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(cros_ec_rtc_driver);
+
+MODULE_DESCRIPTION("RTC driver for Chrome OS ECs");
+MODULE_AUTHOR("Stephen Barber <smbarber@chromium.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--
2.9.3
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply related
* [rtc-linux] [PATCH v3 2/4] mfd: cros_ec: Introduce RTC commands and events definitions.
From: Enric Balletbo i Serra @ 2017-07-12 10:13 UTC (permalink / raw)
To: Jonathan Cameron, Lee Jones, bleung
Cc: martinez.javier, Guenter Roeck, Gwendal Grignou, linux-kernel,
linux-iio, rtc-linux, Stephen Barber
In-Reply-To: <20170712101309.6045-1-enric.balletbo@collabora.com>
From: Stephen Barber <smbarber@chromium.org>
The EC can function as a simple RT, this patch adds the RTC related
definitions needed by the rtc-cros-ec driver.
Signed-off-by: Stephen Barber <smbarber@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
include/linux/mfd/cros_ec_commands.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
index 190c8f4..26ef492 100644
--- a/include/linux/mfd/cros_ec_commands.h
+++ b/include/linux/mfd/cros_ec_commands.h
@@ -286,6 +286,9 @@ enum host_event_code {
/* Hang detect logic detected a hang and warm rebooted the AP */
EC_HOST_EVENT_HANG_REBOOT = 21,
+ /* EC RTC event occurred */
+ EC_HOST_EVENT_RTC = 26,
+
/*
* The high bit of the event mask is not used as a host event code. If
* it reads back as set, then the entire event mask should be
@@ -794,6 +797,8 @@ enum ec_feature_code {
EC_FEATURE_USB_MUX = 23,
/* Motion Sensor code has an internal software FIFO */
EC_FEATURE_MOTION_SENSE_FIFO = 24,
+ /* EC has RTC feature that can be controlled by host commands */
+ EC_FEATURE_RTC = 27,
};
#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
@@ -1704,6 +1709,9 @@ struct ec_response_rtc {
#define EC_CMD_RTC_SET_VALUE 0x46
#define EC_CMD_RTC_SET_ALARM 0x47
+/* Pass as param to SET_ALARM to clear the current alarm */
+#define EC_RTC_ALARM_CLEAR 0
+
/*****************************************************************************/
/* Port80 log access */
--
2.9.3
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply related
* [rtc-linux] [PATCH v3 1/4] mfd: cros_ec: Get rid of cros_ec_check_features from cros_ec_dev.
From: Enric Balletbo i Serra @ 2017-07-12 10:13 UTC (permalink / raw)
To: Jonathan Cameron, Lee Jones, bleung
Cc: martinez.javier, Guenter Roeck, Gwendal Grignou, linux-kernel,
linux-iio, rtc-linux
In-Reply-To: <20170712101309.6045-1-enric.balletbo@collabora.com>
The cros_ec_dev driver should be used only to expose the Chrome OS Embedded
Controller to user-space and should not be used to add MFD devices by
calling mfd_add_devices. This patch moves this logic to the MFD cros_ec
driver and removes the MFD bits from the character device driver. Also
makes independent the IIO driver from the character device as also has no
sense.
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
.../iio/common/cros_ec_sensors/cros_ec_sensors.c | 8 -
.../common/cros_ec_sensors/cros_ec_sensors_core.c | 8 +-
drivers/iio/light/cros_ec_light_prox.c | 8 -
drivers/iio/pressure/cros_ec_baro.c | 8 -
drivers/mfd/cros_ec.c | 160 ++++++++++++++++++++
drivers/platform/chrome/cros_ec_dev.c | 161 ---------------------
include/linux/mfd/cros_ec.h | 6 +-
7 files changed, 170 insertions(+), 189 deletions(-)
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index 38e8783..9b53a01 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -191,19 +191,11 @@ static const struct iio_info ec_sensors_info = {
static int cros_ec_sensors_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
- struct cros_ec_device *ec_device;
struct iio_dev *indio_dev;
struct cros_ec_sensors_state *state;
struct iio_chan_spec *channel;
int ret, i;
- if (!ec_dev || !ec_dev->ec_dev) {
- dev_warn(&pdev->dev, "No CROS EC device found.\n");
- return -EINVAL;
- }
- ec_device = ec_dev->ec_dev;
-
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
index 416cae5..0cdb64a 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -41,12 +41,13 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
{
struct device *dev = &pdev->dev;
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
- struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_device *ec_dev = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
platform_set_drvdata(pdev, indio_dev);
- state->ec = ec->ec_dev;
+ state->ec = ec_dev;
+
state->msg = devm_kzalloc(&pdev->dev,
max((u16)sizeof(struct ec_params_motion_sense),
state->ec->max_response), GFP_KERNEL);
@@ -59,7 +60,8 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
/* Set up the host command structure. */
state->msg->version = 2;
- state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+ state->msg->command = EC_CMD_MOTION_SENSE_CMD +
+ sensor_platform->cmd_offset;
state->msg->outsize = sizeof(struct ec_params_motion_sense);
indio_dev->dev.parent = &pdev->dev;
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index 7217223..2133ddc 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -181,19 +181,11 @@ static const struct iio_info cros_ec_light_prox_info = {
static int cros_ec_light_prox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
- struct cros_ec_device *ec_device;
struct iio_dev *indio_dev;
struct cros_ec_light_prox_state *state;
struct iio_chan_spec *channel;
int ret;
- if (!ec_dev || !ec_dev->ec_dev) {
- dev_warn(dev, "No CROS EC device found.\n");
- return -EINVAL;
- }
- ec_device = ec_dev->ec_dev;
-
indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
index 48b2a30..dbea18b 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -126,19 +126,11 @@ static const struct iio_info cros_ec_baro_info = {
static int cros_ec_baro_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
- struct cros_ec_device *ec_device;
struct iio_dev *indio_dev;
struct cros_ec_baro_state *state;
struct iio_chan_spec *channel;
int ret;
- if (!ec_dev || !ec_dev->ec_dev) {
- dev_warn(dev, "No CROS EC device found.\n");
- return -EINVAL;
- }
- ec_device = ec_dev->ec_dev;
-
indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index b0ca5a4c..75a27a6 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -91,6 +91,160 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
return cros_ec_cmd_xfer(ec_dev, &buf.msg);
}
+static int cros_ec_check_features(struct cros_ec_device *ec_dev, int feature)
+{
+ struct cros_ec_command *msg;
+ int ret;
+
+ if (ec_dev->features[0] == -1U && ec_dev->features[1] == -1U) {
+ /* features bitmap not read yet */
+
+ msg = kmalloc(sizeof(*msg) + sizeof(ec_dev->features),
+ GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_GET_FEATURES + ec_p.cmd_offset;
+ msg->insize = sizeof(ec_dev->features);
+ msg->outsize = 0;
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec_dev->dev, "cannot get EC features: %d/%d\n",
+ ret, msg->result);
+ memset(ec_dev->features, 0, sizeof(ec_dev->features));
+ }
+
+ memcpy(ec_dev->features, msg->data, sizeof(ec_dev->features));
+
+ dev_dbg(ec_dev->dev, "EC features %08x %08x\n",
+ ec_dev->features[0], ec_dev->features[1]);
+
+ kfree(msg);
+ }
+
+ return ec_dev->features[feature / 32] & EC_FEATURE_MASK_0(feature);
+}
+
+static void cros_ec_sensors_register(struct cros_ec_device *ec_dev)
+{
+ /*
+ * Issue a command to get the number of sensor reported.
+ * Build an array of sensors driver and register them all.
+ */
+ int ret, i, id, sensor_num;
+ struct mfd_cell *sensor_cells;
+ struct cros_ec_sensor_platform *sensor_platforms;
+ int sensor_type[MOTIONSENSE_TYPE_MAX];
+ struct ec_params_motion_sense *params;
+ struct ec_response_motion_sense *resp;
+ struct cros_ec_command *msg;
+
+ msg = kzalloc(sizeof(struct cros_ec_command) +
+ max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
+ if (msg == NULL)
+ return;
+
+ msg->version = 2;
+ msg->command = EC_CMD_MOTION_SENSE_CMD + ec_p.cmd_offset;
+ msg->outsize = sizeof(*params);
+ msg->insize = sizeof(*resp);
+
+ params = (struct ec_params_motion_sense *)msg->data;
+ params->cmd = MOTIONSENSE_CMD_DUMP;
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec_dev->dev, "cannot get EC sensor information: %d/%d\n",
+ ret, msg->result);
+ goto error;
+ }
+
+ resp = (struct ec_response_motion_sense *)msg->data;
+ sensor_num = resp->dump.sensor_count;
+ /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
+ sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
+ GFP_KERNEL);
+ if (sensor_cells == NULL)
+ goto error;
+
+ sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
+ (sensor_num + 1), GFP_KERNEL);
+ if (sensor_platforms == NULL)
+ goto error_platforms;
+
+ memset(sensor_type, 0, sizeof(sensor_type));
+ id = 0;
+ for (i = 0; i < sensor_num; i++) {
+ params->cmd = MOTIONSENSE_CMD_INFO;
+ params->info.sensor_num = i;
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec_dev->dev, "no info for EC sensor %d : %d/%d\n",
+ i, ret, msg->result);
+ continue;
+ }
+ switch (resp->info.type) {
+ case MOTIONSENSE_TYPE_ACCEL:
+ sensor_cells[id].name = "cros-ec-accel";
+ break;
+ case MOTIONSENSE_TYPE_BARO:
+ sensor_cells[id].name = "cros-ec-baro";
+ break;
+ case MOTIONSENSE_TYPE_GYRO:
+ sensor_cells[id].name = "cros-ec-gyro";
+ break;
+ case MOTIONSENSE_TYPE_MAG:
+ sensor_cells[id].name = "cros-ec-mag";
+ break;
+ case MOTIONSENSE_TYPE_PROX:
+ sensor_cells[id].name = "cros-ec-prox";
+ break;
+ case MOTIONSENSE_TYPE_LIGHT:
+ sensor_cells[id].name = "cros-ec-light";
+ break;
+ case MOTIONSENSE_TYPE_ACTIVITY:
+ sensor_cells[id].name = "cros-ec-activity";
+ break;
+ default:
+ dev_warn(ec_dev->dev, "unknown type %d\n",
+ resp->info.type);
+ continue;
+ }
+ sensor_platforms[id].sensor_num = i;
+ sensor_platforms[id].cmd_offset = ec_p.cmd_offset;
+ sensor_cells[id].id = sensor_type[resp->info.type];
+ sensor_cells[id].platform_data = &sensor_platforms[id];
+ sensor_cells[id].pdata_size =
+ sizeof(struct cros_ec_sensor_platform);
+
+ sensor_type[resp->info.type]++;
+ id++;
+ }
+ if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
+ sensor_platforms[id].sensor_num = sensor_num;
+
+ sensor_cells[id].name = "cros-ec-angle";
+ sensor_cells[id].id = 0;
+ sensor_cells[id].platform_data = &sensor_platforms[id];
+ sensor_cells[id].pdata_size =
+ sizeof(struct cros_ec_sensor_platform);
+ id++;
+ }
+
+ ret = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, sensor_cells,
+ id, NULL, 0, NULL);
+ if (ret)
+ dev_err(ec_dev->dev, "failed to add EC sensors\n");
+
+ kfree(sensor_platforms);
+error_platforms:
+ kfree(sensor_cells);
+error:
+ kfree(msg);
+}
+
int cros_ec_register(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;
@@ -101,6 +255,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
ec_dev->max_request = sizeof(struct ec_params_hello);
ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
ec_dev->max_passthru = 0;
+ ec_dev->features[0] = -1U; /* Not cached yet */
+ ec_dev->features[1] = -1U; /* Not cached yet */
ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
if (!ec_dev->din)
@@ -134,6 +290,10 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
goto fail_mfd;
}
+ /* Check whether this EC is a sensor hub. */
+ if (cros_ec_check_features(ec_dev, EC_FEATURE_MOTION_SENSE))
+ cros_ec_sensors_register(ec_dev);
+
if (ec_dev->max_passthru) {
/*
* Register a PD device as well on top of this device.
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index cf6c4f0..bd07df5 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -90,41 +90,6 @@ static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
return ret;
}
-static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
-{
- struct cros_ec_command *msg;
- int ret;
-
- if (ec->features[0] == -1U && ec->features[1] == -1U) {
- /* features bitmap not read yet */
-
- msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- msg->version = 0;
- msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
- msg->insize = sizeof(ec->features);
- msg->outsize = 0;
-
- ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
- if (ret < 0 || msg->result != EC_RES_SUCCESS) {
- dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
- ret, msg->result);
- memset(ec->features, 0, sizeof(ec->features));
- }
-
- memcpy(ec->features, msg->data, sizeof(ec->features));
-
- dev_dbg(ec->dev, "EC features %08x %08x\n",
- ec->features[0], ec->features[1]);
-
- kfree(msg);
- }
-
- return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
-}
-
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
@@ -268,126 +233,6 @@ static void __remove(struct device *dev)
kfree(ec);
}
-static void cros_ec_sensors_register(struct cros_ec_dev *ec)
-{
- /*
- * Issue a command to get the number of sensor reported.
- * Build an array of sensors driver and register them all.
- */
- int ret, i, id, sensor_num;
- struct mfd_cell *sensor_cells;
- struct cros_ec_sensor_platform *sensor_platforms;
- int sensor_type[MOTIONSENSE_TYPE_MAX];
- struct ec_params_motion_sense *params;
- struct ec_response_motion_sense *resp;
- struct cros_ec_command *msg;
-
- msg = kzalloc(sizeof(struct cros_ec_command) +
- max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
- if (msg == NULL)
- return;
-
- msg->version = 2;
- msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
- msg->outsize = sizeof(*params);
- msg->insize = sizeof(*resp);
-
- params = (struct ec_params_motion_sense *)msg->data;
- params->cmd = MOTIONSENSE_CMD_DUMP;
-
- ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
- if (ret < 0 || msg->result != EC_RES_SUCCESS) {
- dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
- ret, msg->result);
- goto error;
- }
-
- resp = (struct ec_response_motion_sense *)msg->data;
- sensor_num = resp->dump.sensor_count;
- /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
- sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
- GFP_KERNEL);
- if (sensor_cells == NULL)
- goto error;
-
- sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
- (sensor_num + 1), GFP_KERNEL);
- if (sensor_platforms == NULL)
- goto error_platforms;
-
- memset(sensor_type, 0, sizeof(sensor_type));
- id = 0;
- for (i = 0; i < sensor_num; i++) {
- params->cmd = MOTIONSENSE_CMD_INFO;
- params->info.sensor_num = i;
- ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
- if (ret < 0 || msg->result != EC_RES_SUCCESS) {
- dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
- i, ret, msg->result);
- continue;
- }
- switch (resp->info.type) {
- case MOTIONSENSE_TYPE_ACCEL:
- sensor_cells[id].name = "cros-ec-accel";
- break;
- case MOTIONSENSE_TYPE_BARO:
- sensor_cells[id].name = "cros-ec-baro";
- break;
- case MOTIONSENSE_TYPE_GYRO:
- sensor_cells[id].name = "cros-ec-gyro";
- break;
- case MOTIONSENSE_TYPE_MAG:
- sensor_cells[id].name = "cros-ec-mag";
- break;
- case MOTIONSENSE_TYPE_PROX:
- sensor_cells[id].name = "cros-ec-prox";
- break;
- case MOTIONSENSE_TYPE_LIGHT:
- sensor_cells[id].name = "cros-ec-light";
- break;
- case MOTIONSENSE_TYPE_ACTIVITY:
- sensor_cells[id].name = "cros-ec-activity";
- break;
- default:
- dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
- continue;
- }
- sensor_platforms[id].sensor_num = i;
- sensor_cells[id].id = sensor_type[resp->info.type];
- sensor_cells[id].platform_data = &sensor_platforms[id];
- sensor_cells[id].pdata_size =
- sizeof(struct cros_ec_sensor_platform);
-
- sensor_type[resp->info.type]++;
- id++;
- }
- if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
- sensor_platforms[id].sensor_num = sensor_num;
-
- sensor_cells[id].name = "cros-ec-angle";
- sensor_cells[id].id = 0;
- sensor_cells[id].platform_data = &sensor_platforms[id];
- sensor_cells[id].pdata_size =
- sizeof(struct cros_ec_sensor_platform);
- id++;
- }
- if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
- sensor_cells[id].name = "cros-ec-ring";
- id++;
- }
-
- ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
- NULL, 0, NULL);
- if (ret)
- dev_err(ec->dev, "failed to add EC sensors\n");
-
- kfree(sensor_platforms);
-error_platforms:
- kfree(sensor_cells);
-error:
- kfree(msg);
-}
-
static int ec_device_probe(struct platform_device *pdev)
{
int retval = -ENOMEM;
@@ -402,8 +247,6 @@ static int ec_device_probe(struct platform_device *pdev)
ec->ec_dev = dev_get_drvdata(dev->parent);
ec->dev = dev;
ec->cmd_offset = ec_platform->cmd_offset;
- ec->features[0] = -1U; /* Not cached yet */
- ec->features[1] = -1U; /* Not cached yet */
device_initialize(&ec->class_dev);
cdev_init(&ec->cdev, &fops);
@@ -432,10 +275,6 @@ static int ec_device_probe(struct platform_device *pdev)
if (cros_ec_debugfs_init(ec))
dev_warn(dev, "failed to create debugfs directory\n");
- /* check whether this EC is a sensor hub. */
- if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
- cros_ec_sensors_register(ec);
-
/* Take control of the lightbar from the EC. */
lb_manual_suspend_ctrl(ec, 1);
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index 4e887ba..a4138a5 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -115,6 +115,7 @@ struct cros_ec_command {
* @event_notifier: interrupt event notifier for transport devices.
* @event_data: raw payload transferred with the MKBP event.
* @event_size: size in bytes of the event data.
+ * @features: stores the EC features.
*/
struct cros_ec_device {
@@ -150,15 +151,19 @@ struct cros_ec_device {
struct ec_response_get_next_event event_data;
int event_size;
u32 host_event_wake_mask;
+ u32 features[2];
};
/**
* struct cros_ec_sensor_platform - ChromeOS EC sensor platform information
*
* @sensor_num: Id of the sensor, as reported by the EC.
+ * @cmd_offset: offset to apply for each command. Set when
+ * registering a devicde behind another one.
*/
struct cros_ec_sensor_platform {
u8 sensor_num;
+ u16 cmd_offset;
};
/* struct cros_ec_platform - ChromeOS EC platform information
@@ -192,7 +197,6 @@ struct cros_ec_dev {
struct device *dev;
struct cros_ec_debugfs *debug_info;
u16 cmd_offset;
- u32 features[2];
};
/**
--
2.9.3
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox