123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/bitfield.h>
- #include <linux/bitops.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/property.h>
- #include <linux/slab.h>
- #include <linux/iio/iio.h>
- #include <linux/soc/qcom/pmic_glink.h>
- #define MSG_OWNER_SMB_ADC 32784
- #define MSG_TYPE_REQ_RESP 1
- #define SMB_ADC_READ_REQ_OP 0x49
- #define ADC_READ_WAIT_TIME_MS 1000
- enum glink_adc_channel {
- GLINK_ADC_CHAN_IIN = 1,
- GLINK_ADC_CHAN_ICHG = 2,
- GLINK_ADC_CHAN_DIE_TEMP = 3,
- GLINK_ADC_CHAN_MAX
- };
- enum smb_adc_read_status {
- SMB_ADC_READ_STATUS_SUCCESS = 0,
- SMB_ADC_READ_STATUS_ERR_NO_OPCODE = 0x100,
- SMB_ADC_READ_STATUS_ERR_FAILED = 0x200,
- SMB_ADC_READ_STATUS_ERR_NO_PMIC = 0x201,
- SMB_ADC_READ_STATUS_ERR_INVALID_PARAM = 0x202,
- };
- struct smb_adc_read_req_msg {
- struct pmic_glink_hdr hdr;
- u32 bus_id;
- u32 pmic_id;
- u32 chan;
- };
- struct smb_adc_read_resp_msg {
- struct pmic_glink_hdr hdr;
- u32 bus_id;
- u32 pmic_id;
- u32 chan;
- u32 raw_data;
- u32 conv_data;
- u32 status;
- };
- struct glink_adc_dev {
- struct pmic_glink_client *client;
- struct device *dev;
- struct mutex lock;
- struct completion ack;
- struct iio_chan_spec *iio_chans;
- unsigned int nchannels;
- struct smb_adc_read_resp_msg read_msg;
- };
- #define ADC_GLINK_CHAN(hwchan) FIELD_GET(GENMASK(7, 0), hwchan)
- #define ADC_GLINK_PMIC_ID(hwchan) FIELD_GET(GENMASK(15, 8), hwchan)
- #define ADC_GLINK_BUS_ID(hwchan) FIELD_GET(GENMASK(31, 16), hwchan)
- static void glink_adc_handle_read_resp(struct glink_adc_dev *adc,
- struct smb_adc_read_resp_msg *read_resp,
- size_t len)
- {
- if (len != sizeof(*read_resp)) {
- dev_err(adc->dev, "Invalid read response, glink packet size=%zu\n",
- len);
- return;
- }
- memcpy(&adc->read_msg, read_resp, sizeof(adc->read_msg));
- complete(&adc->ack);
- }
- static int glink_adc_callback(void *priv, void *data, size_t len)
- {
- struct glink_adc_dev *adc = priv;
- struct pmic_glink_hdr *hdr = data;
- dev_dbg(adc->dev, "owner: %u type: %u opcode: %#x len: %zu\n",
- hdr->owner, hdr->type, hdr->opcode, len);
- switch (hdr->opcode) {
- case SMB_ADC_READ_REQ_OP:
- glink_adc_handle_read_resp(adc, data, len);
- break;
- default:
- dev_err(adc->dev, "Unknown opcode %u\n", hdr->opcode);
- break;
- }
- return 0;
- }
- static int glink_adc_read_channel(struct glink_adc_dev *adc,
- struct iio_chan_spec const *chan,
- int *conv_data, int *raw_data)
- {
- struct smb_adc_read_req_msg msg = {{0}};
- int ret;
- msg.hdr.owner = MSG_OWNER_SMB_ADC;
- msg.hdr.type = MSG_TYPE_REQ_RESP;
- msg.hdr.opcode = SMB_ADC_READ_REQ_OP;
- msg.bus_id = ADC_GLINK_BUS_ID(chan->channel);
- msg.pmic_id = ADC_GLINK_PMIC_ID(chan->channel);
- msg.chan = ADC_GLINK_CHAN(chan->channel);
- mutex_lock(&adc->lock);
- reinit_completion(&adc->ack);
- ret = pmic_glink_write(adc->client, &msg, sizeof(msg));
- if (ret)
- goto done;
- ret = wait_for_completion_timeout(&adc->ack,
- msecs_to_jiffies(ADC_READ_WAIT_TIME_MS));
- if (!ret) {
- dev_err(adc->dev, "Error, ADC conversion timed out\n");
- ret = -ETIMEDOUT;
- goto done;
- }
- if (adc->read_msg.status != SMB_ADC_READ_STATUS_SUCCESS) {
- dev_err(adc->dev, "glink ADC read failed, bus_id=%u, pmic_id=%u, chan=%u, ret=%u\n",
- adc->read_msg.bus_id, adc->read_msg.pmic_id,
- adc->read_msg.chan, adc->read_msg.status);
- ret = -EIO;
- goto done;
- }
- if (conv_data)
- *conv_data = adc->read_msg.conv_data;
- if (raw_data)
- *raw_data = adc->read_msg.raw_data;
- ret = 0;
- done:
- mutex_unlock(&adc->lock);
- return ret;
- }
- static int glink_adc_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
- {
- struct glink_adc_dev *adc = iio_priv(indio_dev);
- int ret;
- switch (mask) {
- case IIO_CHAN_INFO_PROCESSED:
- ret = glink_adc_read_channel(adc, chan, val, NULL);
- if (ret < 0)
- return ret;
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_RAW:
- ret = glink_adc_read_channel(adc, chan, NULL, val);
- if (ret < 0)
- return ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static int glink_adc_fwnode_xlate(struct iio_dev *indio_dev,
- const struct fwnode_reference_args *iiospec)
- {
- int i;
- for (i = 0; i < indio_dev->num_channels; i++) {
- if (indio_dev->channels[i].channel == iiospec->args[0])
- return i;
- }
- return -EINVAL;
- }
- static const struct iio_info glink_adc_info = {
- .read_raw = glink_adc_read_raw,
- .fwnode_xlate = glink_adc_fwnode_xlate,
- };
- #define GLINK_ADC_CHAN(_name, _type) \
- { \
- .datasheet_name = _name, \
- .type = _type, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) \
- | BIT(IIO_CHAN_INFO_RAW), \
- } \
- static const struct iio_chan_spec glink_adc_channels[] = {
- [GLINK_ADC_CHAN_IIN] = GLINK_ADC_CHAN("iin", IIO_CURRENT),
- [GLINK_ADC_CHAN_ICHG] = GLINK_ADC_CHAN("ichg", IIO_CURRENT),
- [GLINK_ADC_CHAN_DIE_TEMP] = GLINK_ADC_CHAN("die_temp", IIO_TEMP),
- };
- static int glink_adc_get_dt_channel_data(struct glink_adc_dev *adc,
- struct fwnode_handle *fwnode,
- struct iio_chan_spec *iio_chan)
- {
- u32 reg, chan;
- int ret;
- ret = fwnode_property_read_u32(fwnode, "reg", ®);
- if (ret < 0) {
- dev_err(adc->dev, "missing channel number %s, ret=%d\n",
- fwnode_get_name(fwnode), ret);
- return ret;
- }
- chan = ADC_GLINK_CHAN(reg);
- if (chan == 0 || chan >= GLINK_ADC_CHAN_MAX ||
- !glink_adc_channels[chan].datasheet_name) {
- dev_err(adc->dev, "%s invalid channel number %u\n",
- fwnode_get_name(fwnode), chan);
- return -EINVAL;
- }
- iio_chan->channel = reg;
- iio_chan->type = glink_adc_channels[chan].type;
- iio_chan->datasheet_name = glink_adc_channels[chan].datasheet_name;
- fwnode_property_read_string(fwnode, "label", &iio_chan->datasheet_name);
- iio_chan->extend_name = iio_chan->datasheet_name;
- iio_chan->info_mask_separate
- = glink_adc_channels[chan].info_mask_separate;
- return 0;
- }
- static int glink_adc_get_dt_data(struct glink_adc_dev *adc)
- {
- struct iio_chan_spec *iio_chan;
- struct fwnode_handle *child;
- int ret;
- adc->nchannels = device_get_child_node_count(adc->dev);
- if (!adc->nchannels) {
- dev_dbg(adc->dev, "no ADC channels specified\n");
- return -EINVAL;
- }
- adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
- sizeof(*adc->iio_chans), GFP_KERNEL);
- if (!adc->iio_chans)
- return -ENOMEM;
- iio_chan = adc->iio_chans;
- device_for_each_child_node(adc->dev, child) {
- ret = glink_adc_get_dt_channel_data(adc, child, iio_chan);
- if (ret < 0) {
- fwnode_handle_put(child);
- return ret;
- }
- iio_chan++;
- }
- return 0;
- }
- static int glink_adc_probe(struct platform_device *pdev)
- {
- struct glink_adc_dev *adc;
- struct iio_dev *indio_dev;
- struct pmic_glink_client_data client_data = { };
- int ret;
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
- if (!indio_dev)
- return -ENOMEM;
- adc = iio_priv(indio_dev);
- adc->dev = &pdev->dev;
- mutex_init(&adc->lock);
- init_completion(&adc->ack);
- platform_set_drvdata(pdev, adc);
- client_data.id = MSG_OWNER_SMB_ADC;
- client_data.name = "adc";
- client_data.msg_cb = glink_adc_callback;
- client_data.priv = adc;
- ret = glink_adc_get_dt_data(adc);
- if (ret < 0)
- return ret;
- adc->client = pmic_glink_register_client(&pdev->dev, &client_data);
- if (IS_ERR(adc->client)) {
- ret = PTR_ERR(adc->client);
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Error registering glink ADC, ret=%d\n",
- ret);
- return ret;
- }
- indio_dev->dev.parent = &pdev->dev;
- indio_dev->dev.of_node = pdev->dev.of_node;
- indio_dev->name = pdev->name;
- indio_dev->info = &glink_adc_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = adc->iio_chans;
- indio_dev->num_channels = adc->nchannels;
- ret = devm_iio_device_register(&pdev->dev, indio_dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "iio device registration failed, ret=%d\n",
- ret);
- goto fail;
- }
- return 0;
- fail:
- pmic_glink_unregister_client(adc->client);
- return ret;
- }
- static int glink_adc_remove(struct platform_device *pdev)
- {
- struct glink_adc_dev *adc = platform_get_drvdata(pdev);
- pmic_glink_unregister_client(adc->client);
- return 0;
- }
- static const struct of_device_id glink_adc_match_table[] = {
- { .compatible = "qcom,glink-adc", },
- {}
- };
- MODULE_DEVICE_TABLE(of, glink_adc_match_table);
- static struct platform_driver glink_adc_driver = {
- .driver = {
- .name = "glink_adc",
- .of_match_table = glink_adc_match_table,
- },
- .probe = glink_adc_probe,
- .remove = glink_adc_remove,
- };
- module_platform_driver(glink_adc_driver);
- MODULE_DESCRIPTION("Glink ADC Driver");
- MODULE_LICENSE("GPL");
|