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; }
static int hdaps_resume(struct platform_device *dev) { return hdaps_device_init(); }
/** * 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); }