static int gp2ap002_probe()

in light/gp2ap002.c [428:620]


static int gp2ap002_probe(struct i2c_client *client,
			  const struct i2c_device_id *id)
{
	struct gp2ap002 *gp2ap002;
	struct iio_dev *indio_dev;
	struct device *dev = &client->dev;
	enum iio_chan_type ch_type;
	static const struct regmap_config config = {
		.reg_bits = 8,
		.val_bits = 8,
		.max_register = GP2AP002_CON,
	};
	struct regmap *regmap;
	int num_chan;
	const char *compat;
	u8 val;
	int ret;

	indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002));
	if (!indio_dev)
		return -ENOMEM;
	i2c_set_clientdata(client, indio_dev);

	gp2ap002 = iio_priv(indio_dev);
	gp2ap002->dev = dev;

	/*
	 * Check the device compatible like this makes it possible to use
	 * ACPI PRP0001 for registering the sensor using device tree
	 * properties.
	 */
	ret = device_property_read_string(dev, "compatible", &compat);
	if (ret) {
		dev_err(dev, "cannot check compatible\n");
		return ret;
	}
	gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f");

	regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config);
	if (IS_ERR(regmap)) {
		dev_err(dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap));
		return PTR_ERR(regmap);
	}
	gp2ap002->map = regmap;

	/*
	 * The hysteresis settings are coded into the device tree as values
	 * to be written into the hysteresis register. The datasheet defines
	 * modes "A", "B1" and "B2" with fixed values to be use but vendor
	 * code trees for actual devices are tweaking these values and refer to
	 * modes named things like "B1.5". To be able to support any devices,
	 * we allow passing an arbitrary hysteresis setting for "near" and
	 * "far".
	 */

	/* Check the device tree for the IR LED hysteresis */
	ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis",
				      &val);
	if (ret) {
		dev_err(dev, "failed to obtain proximity far setting\n");
		return ret;
	}
	dev_dbg(dev, "proximity far setting %02x\n", val);
	gp2ap002->hys_far = val;

	ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis",
				      &val);
	if (ret) {
		dev_err(dev, "failed to obtain proximity close setting\n");
		return ret;
	}
	dev_dbg(dev, "proximity close setting %02x\n", val);
	gp2ap002->hys_close = val;

	/* The GP2AP002A00F has a light sensor too */
	if (!gp2ap002->is_gp2ap002s00f) {
		gp2ap002->alsout = devm_iio_channel_get(dev, "alsout");
		if (IS_ERR(gp2ap002->alsout)) {
			ret = PTR_ERR(gp2ap002->alsout);
			ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret;
			return dev_err_probe(dev, ret, "failed to get ALSOUT ADC channel\n");
		}
		ret = iio_get_channel_type(gp2ap002->alsout, &ch_type);
		if (ret < 0)
			return ret;
		if (ch_type != IIO_CURRENT) {
			dev_err(dev,
				"wrong type of IIO channel specified for ALSOUT\n");
			return -EINVAL;
		}
	}

	gp2ap002->vdd = devm_regulator_get(dev, "vdd");
	if (IS_ERR(gp2ap002->vdd))
		return dev_err_probe(dev, PTR_ERR(gp2ap002->vdd),
				     "failed to get VDD regulator\n");

	gp2ap002->vio = devm_regulator_get(dev, "vio");
	if (IS_ERR(gp2ap002->vio))
		return dev_err_probe(dev, PTR_ERR(gp2ap002->vio),
				     "failed to get VIO regulator\n");

	/* Operating voltage 2.4V .. 3.6V according to datasheet */
	ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000);
	if (ret) {
		dev_err(dev, "failed to sett VDD voltage\n");
		return ret;
	}

	/* VIO should be between 1.65V and VDD */
	ret = regulator_get_voltage(gp2ap002->vdd);
	if (ret < 0) {
		dev_err(dev, "failed to get VDD voltage\n");
		return ret;
	}
	ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret);
	if (ret) {
		dev_err(dev, "failed to set VIO voltage\n");
		return ret;
	}

	ret = regulator_enable(gp2ap002->vdd);
	if (ret) {
		dev_err(dev, "failed to enable VDD regulator\n");
		return ret;
	}
	ret = regulator_enable(gp2ap002->vio);
	if (ret) {
		dev_err(dev, "failed to enable VIO regulator\n");
		goto out_disable_vdd;
	}

	msleep(20);

	/*
	 * Initialize the device and signal to runtime PM that now we are
	 * definitely up and using power.
	 */
	ret = gp2ap002_init(gp2ap002);
	if (ret) {
		dev_err(dev, "initialization failed\n");
		goto out_disable_vio;
	}
	pm_runtime_get_noresume(dev);
	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);
	gp2ap002->enabled = false;

	ret = devm_request_threaded_irq(dev, client->irq, NULL,
					gp2ap002_prox_irq, IRQF_ONESHOT,
					"gp2ap002", indio_dev);
	if (ret) {
		dev_err(dev, "unable to request IRQ\n");
		goto out_put_pm;
	}
	gp2ap002->irq = client->irq;

	/*
	 * As the device takes 20 ms + regulator delay to come up with a fresh
	 * measurement after power-on, do not shut it down unnecessarily.
	 * Set autosuspend to a one second.
	 */
	pm_runtime_set_autosuspend_delay(dev, 1000);
	pm_runtime_use_autosuspend(dev);
	pm_runtime_put(dev);

	indio_dev->info = &gp2ap002_info;
	indio_dev->name = "gp2ap002";
	indio_dev->channels = gp2ap002_channels;
	/* Skip light channel for the proximity-only sensor */
	num_chan = ARRAY_SIZE(gp2ap002_channels);
	if (gp2ap002->is_gp2ap002s00f)
		num_chan--;
	indio_dev->num_channels = num_chan;
	indio_dev->modes = INDIO_DIRECT_MODE;

	ret = iio_device_register(indio_dev);
	if (ret)
		goto out_disable_pm;
	dev_dbg(dev, "Sharp GP2AP002 probed successfully\n");

	return 0;

out_put_pm:
	pm_runtime_put_noidle(dev);
out_disable_pm:
	pm_runtime_disable(dev);
out_disable_vio:
	regulator_disable(gp2ap002->vio);
out_disable_vdd:
	regulator_disable(gp2ap002->vdd);
	return ret;
}