drivers/iio/accel/bmc150-accel-i2c.c

Source file repositories/reference/linux-study-clean/drivers/iio/accel/bmc150-accel-i2c.c

File Facts

System
Linux kernel
Corpus path
drivers/iio/accel/bmc150-accel-i2c.c
Extension
.c
Size
8818 bytes
Lines
295
Domain
Driver Families
Bucket
drivers/iio
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct dual250e_set_angle_args {
	u32 aux0;
	u32 ang0;
	u32 rawx;
	u32 rawy;
	u32 rawz;
} __packed;

static bool bmc150_acpi_set_angle_dsm(struct i2c_client *client, u32 aux0, u32 ang0)
{
	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
	struct dual250e_set_angle_args args = {
		.aux0 = aux0,
		.ang0 = ang0,
	};
	union acpi_object args_obj, *obj;
	guid_t guid;

	if (!acpi_dev_hid_uid_match(adev, "DUAL250E", NULL))
		return false;

	guid_parse(BMC150_DSM_GUID, &guid);

	if (!acpi_check_dsm(adev->handle, &guid, 0, BIT(DUAL250E_SET_ANGLE_FN_INDEX)))
		return false;

	/*
	 * Note this triggers the following warning:
	 * "ACPI Warning: \_SB.PCI0.I2C2.ACC1._DSM: Argument #4 type mismatch -
	 *                Found [Buffer], ACPI requires [Package]"
	 * This is unavoidable since the _DSM implementation expects a "naked"
	 * buffer, so wrapping it in a package will _not_ work.
	 */
	args_obj.type = ACPI_TYPE_BUFFER;
	args_obj.buffer.length = sizeof(args);
	args_obj.buffer.pointer = (u8 *)&args;

	obj = acpi_evaluate_dsm(adev->handle, &guid, 0, DUAL250E_SET_ANGLE_FN_INDEX, &args_obj);
	if (!obj) {
		dev_err(&client->dev, "Failed to call DSM to enable keyboard and touchpad\n");
		return false;
	}

	ACPI_FREE(obj);
	return true;
}

static bool bmc150_acpi_enable_keyboard(struct i2c_client *client)
{
	/*
	 * The EC must see a change for it to re-enable the kbd, so first
	 * set the angle to 270° (tent/stand mode) and then change it to
	 * 90° (laptop mode).
	 */
	if (!bmc150_acpi_set_angle_dsm(client, 0, 270))
		return false;

	/* The EC needs some time to notice the angle being changed */
	msleep(100);

	return bmc150_acpi_set_angle_dsm(client, 0, 90);
}

static void bmc150_acpi_resume_work(struct work_struct *work)
{
	struct bmc150_accel_data *data =
		container_of(work, struct bmc150_accel_data, resume_work.work);

	bmc150_acpi_enable_keyboard(data->second_device);
}

static void bmc150_acpi_resume_handler(struct device *dev)
{
	struct bmc150_accel_data *data = iio_priv(dev_get_drvdata(dev));

	/*
	 * Delay the bmc150_acpi_enable_keyboard() call till after the system
	 * resume has completed, otherwise it will not work.
	 */
	schedule_delayed_work(&data->resume_work, msecs_to_jiffies(1000));
}

/*
 * Some acpi_devices describe 2 accelerometers in a single ACPI device,
 * try instantiating a second i2c_client for an I2cSerialBusV2 ACPI resource
 * with index 1.
 */
static void bmc150_acpi_dual_accel_probe(struct i2c_client *client)
{
	struct bmc150_accel_data *data = iio_priv(i2c_get_clientdata(client));

Annotation

Implementation Notes