17b3d4f44SNick Crews // SPDX-License-Identifier: GPL-2.0
27b3d4f44SNick Crews /*
37b3d4f44SNick Crews  * Core driver for Wilco Embedded Controller
47b3d4f44SNick Crews  *
57b3d4f44SNick Crews  * Copyright 2018 Google LLC
67b3d4f44SNick Crews  *
77b3d4f44SNick Crews  * This is the entry point for the drivers that control the Wilco EC.
87b3d4f44SNick Crews  */
97b3d4f44SNick Crews 
107b3d4f44SNick Crews #include <linux/acpi.h>
117b3d4f44SNick Crews #include <linux/device.h>
127b3d4f44SNick Crews #include <linux/ioport.h>
137b3d4f44SNick Crews #include <linux/module.h>
147b3d4f44SNick Crews #include <linux/platform_data/wilco-ec.h>
157b3d4f44SNick Crews #include <linux/platform_device.h>
167b3d4f44SNick Crews 
177b3d4f44SNick Crews #include "../cros_ec_lpc_mec.h"
187b3d4f44SNick Crews 
197b3d4f44SNick Crews #define DRV_NAME "wilco-ec"
207b3d4f44SNick Crews 
wilco_get_resource(struct platform_device * pdev,int index)217b3d4f44SNick Crews static struct resource *wilco_get_resource(struct platform_device *pdev,
227b3d4f44SNick Crews 					   int index)
237b3d4f44SNick Crews {
247b3d4f44SNick Crews 	struct device *dev = &pdev->dev;
257b3d4f44SNick Crews 	struct resource *res;
267b3d4f44SNick Crews 
277b3d4f44SNick Crews 	res = platform_get_resource(pdev, IORESOURCE_IO, index);
287b3d4f44SNick Crews 	if (!res) {
297b3d4f44SNick Crews 		dev_dbg(dev, "Couldn't find IO resource %d\n", index);
307b3d4f44SNick Crews 		return res;
317b3d4f44SNick Crews 	}
327b3d4f44SNick Crews 
337b3d4f44SNick Crews 	return devm_request_region(dev, res->start, resource_size(res),
347b3d4f44SNick Crews 				   dev_name(dev));
357b3d4f44SNick Crews }
367b3d4f44SNick Crews 
wilco_ec_probe(struct platform_device * pdev)377b3d4f44SNick Crews static int wilco_ec_probe(struct platform_device *pdev)
387b3d4f44SNick Crews {
397b3d4f44SNick Crews 	struct device *dev = &pdev->dev;
407b3d4f44SNick Crews 	struct wilco_ec_device *ec;
410d2f2a3dSNick Crews 	int ret;
427b3d4f44SNick Crews 
437b3d4f44SNick Crews 	ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
447b3d4f44SNick Crews 	if (!ec)
457b3d4f44SNick Crews 		return -ENOMEM;
467b3d4f44SNick Crews 
477b3d4f44SNick Crews 	platform_set_drvdata(pdev, ec);
487b3d4f44SNick Crews 	ec->dev = dev;
497b3d4f44SNick Crews 	mutex_init(&ec->mailbox_lock);
507b3d4f44SNick Crews 
512ad1f7a9SNick Crews 	ec->data_size = sizeof(struct wilco_ec_response) + EC_MAILBOX_DATA_SIZE;
527b3d4f44SNick Crews 	ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL);
537b3d4f44SNick Crews 	if (!ec->data_buffer)
547b3d4f44SNick Crews 		return -ENOMEM;
557b3d4f44SNick Crews 
567b3d4f44SNick Crews 	/* Prepare access to IO regions provided by ACPI */
577b3d4f44SNick Crews 	ec->io_data = wilco_get_resource(pdev, 0);	/* Host Data */
587b3d4f44SNick Crews 	ec->io_command = wilco_get_resource(pdev, 1);	/* Host Command */
597b3d4f44SNick Crews 	ec->io_packet = wilco_get_resource(pdev, 2);	/* MEC EMI */
607b3d4f44SNick Crews 	if (!ec->io_data || !ec->io_command || !ec->io_packet)
617b3d4f44SNick Crews 		return -ENODEV;
627b3d4f44SNick Crews 
637b3d4f44SNick Crews 	/* Initialize cros_ec register interface for communication */
647b3d4f44SNick Crews 	cros_ec_lpc_mec_init(ec->io_packet->start,
657b3d4f44SNick Crews 			     ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
667b3d4f44SNick Crews 
67b787bb12SNick Crews 	/*
68b787bb12SNick Crews 	 * Register a child device that will be found by the debugfs driver.
69b787bb12SNick Crews 	 * Ignore failure.
70b787bb12SNick Crews 	 */
71b787bb12SNick Crews 	ec->debugfs_pdev = platform_device_register_data(dev,
72b787bb12SNick Crews 							 "wilco-ec-debugfs",
73b787bb12SNick Crews 							 PLATFORM_DEVID_AUTO,
74b787bb12SNick Crews 							 NULL, 0);
75b787bb12SNick Crews 
760d2f2a3dSNick Crews 	/* Register a child device that will be found by the RTC driver. */
770d2f2a3dSNick Crews 	ec->rtc_pdev = platform_device_register_data(dev, "rtc-wilco-ec",
780d2f2a3dSNick Crews 						     PLATFORM_DEVID_AUTO,
790d2f2a3dSNick Crews 						     NULL, 0);
800d2f2a3dSNick Crews 	if (IS_ERR(ec->rtc_pdev)) {
810d2f2a3dSNick Crews 		dev_err(dev, "Failed to create RTC platform device\n");
820d2f2a3dSNick Crews 		ret = PTR_ERR(ec->rtc_pdev);
830d2f2a3dSNick Crews 		goto unregister_debugfs;
840d2f2a3dSNick Crews 	}
850d2f2a3dSNick Crews 
86119a3cb6SDaniel Campello 	/* Set up the keyboard backlight LEDs. */
87119a3cb6SDaniel Campello 	ret = wilco_keyboard_leds_init(ec);
88119a3cb6SDaniel Campello 	if (ret < 0) {
89119a3cb6SDaniel Campello 		dev_err(dev,
90119a3cb6SDaniel Campello 			"Failed to initialize keyboard LEDs: %d\n",
91119a3cb6SDaniel Campello 			ret);
92119a3cb6SDaniel Campello 		goto unregister_rtc;
93119a3cb6SDaniel Campello 	}
94119a3cb6SDaniel Campello 
954c1ca625SNick Crews 	ret = wilco_ec_add_sysfs(ec);
964c1ca625SNick Crews 	if (ret < 0) {
97a532149cSStephen Boyd 		dev_err(dev, "Failed to create sysfs entries: %d\n", ret);
984c1ca625SNick Crews 		goto unregister_rtc;
994c1ca625SNick Crews 	}
1004c1ca625SNick Crews 
1013c4d77b6SNick Crews 	/* Register child device to be found by charger config driver. */
1023c4d77b6SNick Crews 	ec->charger_pdev = platform_device_register_data(dev, "wilco-charger",
1033c4d77b6SNick Crews 							 PLATFORM_DEVID_AUTO,
1043c4d77b6SNick Crews 							 NULL, 0);
1053c4d77b6SNick Crews 	if (IS_ERR(ec->charger_pdev)) {
1063c4d77b6SNick Crews 		dev_err(dev, "Failed to create charger platform device\n");
1073c4d77b6SNick Crews 		ret = PTR_ERR(ec->charger_pdev);
1083c4d77b6SNick Crews 		goto remove_sysfs;
1093c4d77b6SNick Crews 	}
1103c4d77b6SNick Crews 
1111210d1e6SNick Crews 	/* Register child device that will be found by the telemetry driver. */
1121210d1e6SNick Crews 	ec->telem_pdev = platform_device_register_data(dev, "wilco_telem",
1131210d1e6SNick Crews 						       PLATFORM_DEVID_AUTO,
1141210d1e6SNick Crews 						       ec, sizeof(*ec));
1151210d1e6SNick Crews 	if (IS_ERR(ec->telem_pdev)) {
1161210d1e6SNick Crews 		dev_err(dev, "Failed to create telemetry platform device\n");
1171210d1e6SNick Crews 		ret = PTR_ERR(ec->telem_pdev);
1183c4d77b6SNick Crews 		goto unregister_charge_config;
1191210d1e6SNick Crews 	}
1201210d1e6SNick Crews 
1217b3d4f44SNick Crews 	return 0;
1220d2f2a3dSNick Crews 
1233c4d77b6SNick Crews unregister_charge_config:
1243c4d77b6SNick Crews 	platform_device_unregister(ec->charger_pdev);
1251210d1e6SNick Crews remove_sysfs:
1261210d1e6SNick Crews 	wilco_ec_remove_sysfs(ec);
1274c1ca625SNick Crews unregister_rtc:
1284c1ca625SNick Crews 	platform_device_unregister(ec->rtc_pdev);
1290d2f2a3dSNick Crews unregister_debugfs:
1300d2f2a3dSNick Crews 	if (ec->debugfs_pdev)
1310d2f2a3dSNick Crews 		platform_device_unregister(ec->debugfs_pdev);
1320d2f2a3dSNick Crews 	return ret;
1337b3d4f44SNick Crews }
1347b3d4f44SNick Crews 
wilco_ec_remove(struct platform_device * pdev)1357b3d4f44SNick Crews static int wilco_ec_remove(struct platform_device *pdev)
1367b3d4f44SNick Crews {
137b787bb12SNick Crews 	struct wilco_ec_device *ec = platform_get_drvdata(pdev);
138b787bb12SNick Crews 
139ffd7263eSDaniel Campello 	platform_device_unregister(ec->telem_pdev);
1403c4d77b6SNick Crews 	platform_device_unregister(ec->charger_pdev);
1414c1ca625SNick Crews 	wilco_ec_remove_sysfs(ec);
1420d2f2a3dSNick Crews 	platform_device_unregister(ec->rtc_pdev);
143b787bb12SNick Crews 	if (ec->debugfs_pdev)
144b787bb12SNick Crews 		platform_device_unregister(ec->debugfs_pdev);
1457b3d4f44SNick Crews 	return 0;
1467b3d4f44SNick Crews }
1477b3d4f44SNick Crews 
1487b3d4f44SNick Crews static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
1497b3d4f44SNick Crews 	{ "GOOG000C", 0 },
1507b3d4f44SNick Crews 	{ }
1517b3d4f44SNick Crews };
1527b3d4f44SNick Crews MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
1537b3d4f44SNick Crews 
1547b3d4f44SNick Crews static struct platform_driver wilco_ec_driver = {
1557b3d4f44SNick Crews 	.driver = {
1567b3d4f44SNick Crews 		.name = DRV_NAME,
1577b3d4f44SNick Crews 		.acpi_match_table = wilco_ec_acpi_device_ids,
1587b3d4f44SNick Crews 	},
1597b3d4f44SNick Crews 	.probe = wilco_ec_probe,
1607b3d4f44SNick Crews 	.remove = wilco_ec_remove,
1617b3d4f44SNick Crews };
1627b3d4f44SNick Crews 
1637b3d4f44SNick Crews module_platform_driver(wilco_ec_driver);
1647b3d4f44SNick Crews 
1657b3d4f44SNick Crews MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
1667b3d4f44SNick Crews MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
1677b3d4f44SNick Crews MODULE_LICENSE("GPL v2");
1687b3d4f44SNick Crews MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver");
1697b3d4f44SNick Crews MODULE_ALIAS("platform:" DRV_NAME);
170