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