Пример #1
0
static int hdaps_probe(struct platform_device *dev)
{
	int ret;

	ret = hdaps_device_init();
	if (ret)
		return ret;

	printk(KERN_INFO "hdaps: device successfully initialized.\n");
	return 0;
}
Пример #2
0
static int hdaps_resume(struct platform_device *dev)
{
	return hdaps_device_init();
}
Пример #3
0
/**
 * hdaps_set_power - enable or disable power to the accelerometer.
 * Returns zero on success and negative error code on failure.  Can sleep.
 */
static int hdaps_set_power(int on)
{
	struct thinkpad_ec_row args =
		{ .mask = 0x0003, .val = {0x14, on?0x01:0x00} };
	struct thinkpad_ec_row data = { .mask = 0x8000 };
	int ret = thinkpad_ec_read_row(&args, &data);
	if (ret)
		return ret;
	if (data.val[0xF] != 0x00)
		return -EIO;
	return 0;
}

/**
 * hdaps_set_ec_config - set accelerometer parameters.
 * @ec_rate: embedded controller sampling rate
 * @order: embedded controller running average filter order
 * (Normally we have @ec_rate = sampling_rate * oversampling_ratio.)
 * Returns zero on success and negative error code on failure.  Can sleep.
 */
static int hdaps_set_ec_config(int ec_rate, int order)
{
	struct thinkpad_ec_row args = { .mask = 0x000F,
		.val = {0x10, (u8)ec_rate, (u8)(ec_rate>>8), order} };
	struct thinkpad_ec_row data = { .mask = 0x8000 };
	int ret = thinkpad_ec_read_row(&args, &data);
	pr_debug("setting ec_rate=%d, filter_order=%d\n", ec_rate, order);
	if (ret)
		return ret;
	if (data.val[0xF] == 0x03) {
		pr_warn("config param out of range\n");
		return -EINVAL;
	}
	if (data.val[0xF] == 0x06) {
		pr_warn("config change already pending\n");
		return -EBUSY;
	}
	if (data.val[0xF] != 0x00) {
		pr_warn("config change error, ret=%d\n",
		      data.val[0xF]);
		return -EIO;
	}
	return 0;
}

/**
 * hdaps_get_ec_config - get accelerometer parameters.
 * @ec_rate: embedded controller sampling rate
 * @order: embedded controller running average filter order
 * Returns zero on success and negative error code on failure.  Can sleep.
 */
static int hdaps_get_ec_config(int *ec_rate, int *order)
{
	const struct thinkpad_ec_row args =
		{ .mask = 0x0003, .val = {0x17, 0x82} };
	struct thinkpad_ec_row data = { .mask = 0x801F };
	int ret = thinkpad_ec_read_row(&args, &data);
	if (ret)
		return ret;
	if (data.val[0xF] != 0x00)
		return -EIO;
	if (!(data.val[0x1] & 0x01))
		return -ENXIO; /* accelerometer polling not enabled */
	if (data.val[0x1] & 0x02)
		return -EBUSY; /* config change in progress, retry later */
	*ec_rate = data.val[0x2] | ((int)(data.val[0x3]) << 8);
	*order = data.val[0x4];
	return 0;
}

/**
 * hdaps_get_ec_mode - get EC accelerometer mode
 * Returns zero on success and negative error code on failure.  Can sleep.
 */
static int hdaps_get_ec_mode(u8 *mode)
{
	const struct thinkpad_ec_row args =
		{ .mask = 0x0001, .val = {0x13} };
	struct thinkpad_ec_row data = { .mask = 0x8002 };
	int ret = thinkpad_ec_read_row(&args, &data);
	if (ret)
		return ret;
	if (data.val[0xF] != 0x00) {
		pr_warn("accelerometer not implemented (0x%02x)\n",
		       data.val[0xF]);
		return -EIO;
	}
	*mode = data.val[0x1];
	return 0;
}

/**
 * hdaps_check_ec - checks something about the EC.
 * Follows the clean-room spec for HDAPS; we don't know what it means.
 * Returns zero on success and negative error code on failure.  Can sleep.
 */
static int hdaps_check_ec(void)
{
	const struct thinkpad_ec_row args =
		{ .mask = 0x0003, .val = {0x17, 0x81} };
	struct thinkpad_ec_row data = { .mask = 0x800E };
	int ret = thinkpad_ec_read_row(&args, &data);
	if (ret)
		return  ret;
	if (!((data.val[0x1] == 0x00 && data.val[0x2] == 0x60) || /* cleanroom spec */
	      (data.val[0x1] == 0x01 && data.val[0x2] == 0x00)) || /* seen on T61 */
	    data.val[0x3] != 0x00 || data.val[0xF] != 0x00) {
		pr_warn("hdaps_check_ec: bad response (0x%x,0x%x,0x%x,0x%x)\n",
		       data.val[0x1], data.val[0x2],
		       data.val[0x3], data.val[0xF]);
		return -EIO;
	}
	return 0;
}

/**
 * hdaps_device_init - initialize the accelerometer.
 *
 * Call several embedded controller functions to test and initialize the
 * accelerometer.
 * Returns zero on success and negative error code on failure. Can sleep.
 */
#define FAILED_INIT(msg) pr_err("init failed at: %s\n", msg)
static int hdaps_device_init(void)
{
	int ret;
	u8 mode;

	ret = thinkpad_ec_lock();
	if (ret)
		return ret;

	if (hdaps_get_ec_mode(&mode))
		{ FAILED_INIT("hdaps_get_ec_mode failed"); goto bad; }

	pr_debug("initial mode latch is 0x%02x\n", mode);
	if (mode == 0x00)
		{ FAILED_INIT("accelerometer not available"); goto bad; }

	if (hdaps_check_ec())
		{ FAILED_INIT("hdaps_check_ec failed"); goto bad; }

	if (hdaps_set_power(1))
		{ FAILED_INIT("hdaps_set_power failed"); goto bad; }

	if (hdaps_set_ec_config(sampling_rate*oversampling_ratio,
				running_avg_filter_order))
		{ FAILED_INIT("hdaps_set_ec_config failed"); goto bad; }

	thinkpad_ec_invalidate();
	udelay(200);

	/* Just prefetch instead of reading, to avoid ~1sec delay on load */
	ret = thinkpad_ec_prefetch_row(&ec_accel_args);
	if (ret)
		{ FAILED_INIT("initial prefetch failed"); goto bad; }
	goto good;
bad:
	thinkpad_ec_invalidate();
	ret = -ENXIO;
good:
	stale_readout = 1;
	thinkpad_ec_unlock();
	return ret;
}

/**
 * hdaps_device_shutdown - power off the accelerometer
 * Returns nonzero on failure. Can sleep.
 */
static int hdaps_device_shutdown(void)
{
	int ret;
	ret = hdaps_set_power(0);
	if (ret) {
		pr_warn("cannot power off\n");
		return ret;
	}
	ret = hdaps_set_ec_config(0, 1);
	if (ret)
		pr_warn("cannot stop EC sampling\n");
	return ret;
}

/* Device model stuff */

static int hdaps_probe(struct platform_device *dev)
{
	int ret;

	ret = hdaps_device_init();
	if (ret)
		return ret;

	pr_info("device successfully initialized\n");
	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int hdaps_suspend(struct device *dev)
{
	/* Don't do hdaps polls until resume re-initializes the sensor. */
	del_timer_sync(&hdaps_timer);
	hdaps_device_shutdown(); /* ignore errors, effect is negligible */
	return 0;
}

static int hdaps_resume(struct device *dev)
{
	int ret = hdaps_device_init();
	if (ret)
		return ret;

	mutex_lock(&hdaps_users_mtx);
	if (hdaps_users)
		mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
	mutex_unlock(&hdaps_users_mtx);
	return 0;
}
#endif

static SIMPLE_DEV_PM_OPS(hdaps_pm, hdaps_suspend, hdaps_resume);

static struct platform_driver hdaps_driver = {
	.probe = hdaps_probe,
	.driver	= {
		.name = "hdaps",
		.pm = &hdaps_pm,
	},
};

/**
 * hdaps_calibrate - set our "resting" values.
 * Does its own locking.
 */
static void hdaps_calibrate(void)
{
	needs_calibration = 1;
	hdaps_update();
	/* If that fails, the mousedev poll will take care of things later. */
}

/* Timer handler for updating the input device. Runs in softirq context,
 * so avoid lenghty or blocking operations.
 */
static void hdaps_mousedev_poll(unsigned long unused)
{
	int ret;

	stale_readout = 1;

	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
	if (thinkpad_ec_try_lock())
		goto keep_active;

	ret = __hdaps_update(1); /* fast update, we're in softirq context */
	thinkpad_ec_unlock();
	/* Any of "successful", "not yet ready" and "not prefetched"? */
	if (ret != 0 && ret != -EBUSY && ret != -ENODATA) {
		pr_err("poll failed, disabling updates\n");
		return;
	}

keep_active:
	/* Even if we failed now, pos_x,y may have been updated earlier: */
	input_report_abs(hdaps_idev, ABS_X, pos_x - rest_x);
	input_report_abs(hdaps_idev, ABS_Y, pos_y - rest_y);
	input_sync(hdaps_idev);
	input_report_abs(hdaps_idev_raw, ABS_X, pos_x);
	input_report_abs(hdaps_idev_raw, ABS_Y, pos_y);
	input_sync(hdaps_idev_raw);
	mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
}


/* Sysfs Files */

static ssize_t hdaps_position_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	int ret = hdaps_update();
	if (ret)
		return ret;
	return sprintf(buf, "(%d,%d)\n", pos_x, pos_y);
}