diff options
Diffstat (limited to 'drivers/iio/adc/xilinx-xadc-core.c')
| -rw-r--r-- | drivers/iio/adc/xilinx-xadc-core.c | 213 |
1 files changed, 95 insertions, 118 deletions
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 198d2916266d..e257c1b94a5f 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -17,10 +17,11 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of.h> #include <linux/overflow.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/sysfs.h> @@ -107,6 +108,7 @@ static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500; #define XADC_AXI_INT_ALARM_MASK 0x3c0f #define XADC_FLAGS_BUFFERED BIT(0) +#define XADC_FLAGS_IRQ_OPTIONAL BIT(1) /* * The XADC hardware supports a samplerate of up to 1MSPS. Unfortunately it does @@ -454,6 +456,9 @@ static const struct xadc_ops xadc_zynq_ops = { .interrupt_handler = xadc_zynq_interrupt_handler, .update_alarm = xadc_zynq_update_alarm, .type = XADC_TYPE_S7, + /* Temp in C = (val * 503.975) / 2**bits - 273.15 */ + .temp_scale = 503975, + .temp_offset = 273150, }; static const unsigned int xadc_axi_reg_offsets[] = { @@ -562,8 +567,11 @@ static const struct xadc_ops xadc_7s_axi_ops = { .get_dclk_rate = xadc_axi_get_dclk, .update_alarm = xadc_axi_update_alarm, .interrupt_handler = xadc_axi_interrupt_handler, - .flags = XADC_FLAGS_BUFFERED, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, .type = XADC_TYPE_S7, + /* Temp in C = (val * 503.975) / 2**bits - 273.15 */ + .temp_scale = 503975, + .temp_offset = 273150, }; static const struct xadc_ops xadc_us_axi_ops = { @@ -573,8 +581,14 @@ static const struct xadc_ops xadc_us_axi_ops = { .get_dclk_rate = xadc_axi_get_dclk, .update_alarm = xadc_axi_update_alarm, .interrupt_handler = xadc_axi_interrupt_handler, - .flags = XADC_FLAGS_BUFFERED, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, .type = XADC_TYPE_US, + /** + * Values below are for UltraScale+ (SYSMONE4) using internal reference. + * See https://docs.xilinx.com/v/u/en-US/ug580-ultrascale-sysmon + */ + .temp_scale = 509314, + .temp_offset = 280231, }; static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, @@ -611,20 +625,17 @@ static int xadc_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *mask) { struct xadc *xadc = iio_priv(indio_dev); - size_t new_size, n; + size_t n; void *data; - n = bitmap_weight(mask, indio_dev->masklength); - - if (check_mul_overflow(n, sizeof(*xadc->data), &new_size)) - return -ENOMEM; + n = bitmap_weight(mask, iio_get_masklength(indio_dev)); - data = devm_krealloc(indio_dev->dev.parent, xadc->data, - new_size, GFP_KERNEL); + data = devm_krealloc_array(indio_dev->dev.parent, xadc->data, + n, sizeof(*xadc->data), GFP_KERNEL); if (!data) return -ENOMEM; - memset(data, 0, new_size); + memset(data, 0, n * sizeof(*xadc->data)); xadc->data = data; return 0; @@ -670,8 +681,7 @@ static irqreturn_t xadc_trigger_handler(int irq, void *p) goto out; j = 0; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { chan = xadc_scan_index_to_channel(i); xadc_read_adc_reg(xadc, chan, &xadc->data[j]); j++; @@ -943,11 +953,10 @@ static int xadc_read_raw(struct iio_dev *indio_dev, *val = 1000; break; } - *val2 = chan->scan_type.realbits; + *val2 = bits; return IIO_VAL_FRACTIONAL_LOG2; case IIO_TEMP: - /* Temp in C = (val * 503.975) / 2**bits - 273.15 */ - *val = 503975; + *val = xadc->ops->temp_scale; *val2 = bits; return IIO_VAL_FRACTIONAL_LOG2; default: @@ -955,7 +964,7 @@ static int xadc_read_raw(struct iio_dev *indio_dev, } case IIO_CHAN_INFO_OFFSET: /* Only the temperature channel has an offset */ - *val = -((273150 << bits) / 503975); + *val = -((xadc->ops->temp_offset << bits) / xadc->ops->temp_scale); return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: ret = xadc_read_samplerate(xadc); @@ -1177,28 +1186,28 @@ static const struct of_device_id xadc_of_match_table[] = { .compatible = "xlnx,system-management-wiz-1.3", .data = &xadc_us_axi_ops }, - { }, + { } }; MODULE_DEVICE_TABLE(of, xadc_of_match_table); -static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, - unsigned int *conf) +static int xadc_parse_dt(struct iio_dev *indio_dev, unsigned int *conf, int irq) { struct device *dev = indio_dev->dev.parent; struct xadc *xadc = iio_priv(indio_dev); const struct iio_chan_spec *channel_templates; struct iio_chan_spec *channels, *chan; - struct device_node *chan_node, *child; + struct fwnode_handle *chan_node, *child; unsigned int max_channels; unsigned int num_channels; const char *external_mux; u32 ext_mux_chan; u32 reg; int ret; + int i; *conf = 0; - ret = of_property_read_string(np, "xlnx,external-mux", &external_mux); + ret = device_property_read_string(dev, "xlnx,external-mux", &external_mux); if (ret < 0 || strcasecmp(external_mux, "none") == 0) xadc->external_mux_mode = XADC_EXTERNAL_MUX_NONE; else if (strcasecmp(external_mux, "single") == 0) @@ -1209,8 +1218,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, return -EINVAL; if (xadc->external_mux_mode != XADC_EXTERNAL_MUX_NONE) { - ret = of_property_read_u32(np, "xlnx,external-mux-channel", - &ext_mux_chan); + ret = device_property_read_u32(dev, "xlnx,external-mux-channel", &ext_mux_chan); if (ret < 0) return ret; @@ -1237,46 +1245,52 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, channel_templates = xadc_us_channels; max_channels = ARRAY_SIZE(xadc_us_channels); } - channels = devm_kmemdup(dev, channel_templates, - sizeof(channels[0]) * max_channels, GFP_KERNEL); + channels = devm_kmemdup_array(dev, channel_templates, max_channels, + sizeof(*channel_templates), GFP_KERNEL); if (!channels) return -ENOMEM; num_channels = 9; chan = &channels[9]; - chan_node = of_get_child_by_name(np, "xlnx,channels"); - if (chan_node) { - for_each_child_of_node(chan_node, child) { - if (num_channels >= max_channels) { - of_node_put(child); - break; - } + chan_node = device_get_named_child_node(dev, "xlnx,channels"); + fwnode_for_each_child_node(chan_node, child) { + if (num_channels >= max_channels) { + fwnode_handle_put(child); + break; + } - ret = of_property_read_u32(child, "reg", ®); - if (ret || reg > 16) - continue; + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg > 16) + continue; - if (of_property_read_bool(child, "xlnx,bipolar")) - chan->scan_type.sign = 's'; + if (fwnode_property_read_bool(child, "xlnx,bipolar")) + chan->scan_type.sign = 's'; - if (reg == 0) { - chan->scan_index = 11; - chan->address = XADC_REG_VPVN; - } else { - chan->scan_index = 15 + reg; - chan->address = XADC_REG_VAUX(reg - 1); - } - num_channels++; - chan++; + if (reg == 0) { + chan->scan_index = 11; + chan->address = XADC_REG_VPVN; + } else { + chan->scan_index = 15 + reg; + chan->address = XADC_REG_VAUX(reg - 1); + } + num_channels++; + chan++; + } + fwnode_handle_put(chan_node); + + /* No IRQ => no events */ + if (irq <= 0) { + for (i = 0; i < num_channels; i++) { + channels[i].event_spec = NULL; + channels[i].num_event_specs = 0; } } - of_node_put(chan_node); indio_dev->num_channels = num_channels; - indio_dev->channels = devm_krealloc(dev, channels, - sizeof(*channels) * num_channels, - GFP_KERNEL); + indio_dev->channels = devm_krealloc_array(dev, channels, + num_channels, sizeof(*channels), + GFP_KERNEL); /* If we can't resize the channels array, just use the original */ if (!indio_dev->channels) indio_dev->channels = channels; @@ -1289,13 +1303,6 @@ static const char * const xadc_type_names[] = { [XADC_TYPE_US] = "xilinx-system-monitor", }; -static void xadc_clk_disable_unprepare(void *data) -{ - struct clk *clk = data; - - clk_disable_unprepare(clk); -} - static void xadc_cancel_delayed_work(void *data) { struct delayed_work *work = data; @@ -1306,7 +1313,7 @@ static void xadc_cancel_delayed_work(void *data) static int xadc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *id; + const struct xadc_ops *ops; struct iio_dev *indio_dev; unsigned int bipolar_mask; unsigned int conf0; @@ -1315,24 +1322,21 @@ static int xadc_probe(struct platform_device *pdev) int irq; int i; - if (!dev->of_node) - return -ENODEV; - - id = of_match_node(xadc_of_match_table, dev->of_node); - if (!id) + ops = device_get_match_data(dev); + if (!ops) return -EINVAL; - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -ENXIO; + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && + (irq != -ENXIO || !(ops->flags & XADC_FLAGS_IRQ_OPTIONAL))) + return irq; indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc)); if (!indio_dev) return -ENOMEM; xadc = iio_priv(indio_dev); - xadc->ops = id->data; - xadc->irq = irq; + xadc->ops = ops; init_completion(&xadc->completion); mutex_init(&xadc->mutex); spin_lock_init(&xadc->lock); @@ -1346,7 +1350,7 @@ static int xadc_probe(struct platform_device *pdev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &xadc_info; - ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0); + ret = xadc_parse_dt(indio_dev, &conf0, irq); if (ret) return ret; @@ -1358,29 +1362,22 @@ static int xadc_probe(struct platform_device *pdev) if (ret) return ret; - xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); - if (IS_ERR(xadc->convst_trigger)) - return PTR_ERR(xadc->convst_trigger); + if (irq > 0) { + xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); + if (IS_ERR(xadc->convst_trigger)) + return PTR_ERR(xadc->convst_trigger); - xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, - "samplerate"); - if (IS_ERR(xadc->samplerate_trigger)) - return PTR_ERR(xadc->samplerate_trigger); + xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, + "samplerate"); + if (IS_ERR(xadc->samplerate_trigger)) + return PTR_ERR(xadc->samplerate_trigger); + } } - xadc->clk = devm_clk_get(dev, NULL); + xadc->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(xadc->clk)) return PTR_ERR(xadc->clk); - ret = clk_prepare_enable(xadc->clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, - xadc_clk_disable_unprepare, xadc->clk); - if (ret) - return ret; - /* * Make sure not to exceed the maximum samplerate since otherwise the * resulting interrupt storm will soft-lock the system. @@ -1397,17 +1394,19 @@ static int xadc_probe(struct platform_device *pdev) } } - ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0, - dev_name(dev), indio_dev); - if (ret) - return ret; + if (irq > 0) { + ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, + 0, dev_name(dev), indio_dev); + if (ret) + return ret; - ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, - &xadc->zynq_unmask_work); - if (ret) - return ret; + ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, + &xadc->zynq_unmask_work); + if (ret) + return ret; + } - ret = xadc->ops->setup(pdev, indio_dev, xadc->irq); + ret = xadc->ops->setup(pdev, indio_dev, irq); if (ret) return ret; @@ -1434,28 +1433,6 @@ static int xadc_probe(struct platform_device *pdev) if (ret) return ret; - /* Disable all alarms */ - ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK, - XADC_CONF1_ALARM_MASK); - if (ret) - return ret; - - /* Set thresholds to min/max */ - for (i = 0; i < 16; i++) { - /* - * Set max voltage threshold and both temperature thresholds to - * 0xffff, min voltage threshold to 0. - */ - if (i % 8 < 4 || i == 7) - xadc->threshold[i] = 0xffff; - else - xadc->threshold[i] = 0; - ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i), - xadc->threshold[i]); - if (ret) - return ret; - } - /* Go to non-buffered mode */ xadc_postdisable(indio_dev); |
