192082a88SKen Xue /* 292082a88SKen Xue * AMD ACPI support for ACPI2platform device. 392082a88SKen Xue * 492082a88SKen Xue * Copyright (c) 2014,2015 AMD Corporation. 592082a88SKen Xue * Authors: Ken Xue <Ken.Xue@amd.com> 692082a88SKen Xue * Wu, Jeff <Jeff.Wu@amd.com> 792082a88SKen Xue * 892082a88SKen Xue * This program is free software; you can redistribute it and/or modify 992082a88SKen Xue * it under the terms of the GNU General Public License version 2 as 1092082a88SKen Xue * published by the Free Software Foundation. 1192082a88SKen Xue */ 1292082a88SKen Xue 1392082a88SKen Xue #include <linux/clk-provider.h> 1492082a88SKen Xue #include <linux/platform_device.h> 1592082a88SKen Xue #include <linux/pm_domain.h> 1692082a88SKen Xue #include <linux/clkdev.h> 1792082a88SKen Xue #include <linux/acpi.h> 1892082a88SKen Xue #include <linux/err.h> 1992082a88SKen Xue #include <linux/pm.h> 2092082a88SKen Xue 2192082a88SKen Xue #include "internal.h" 2292082a88SKen Xue 2392082a88SKen Xue ACPI_MODULE_NAME("acpi_apd"); 2492082a88SKen Xue struct apd_private_data; 2592082a88SKen Xue 2692082a88SKen Xue /** 2792082a88SKen Xue * ACPI_APD_SYSFS : add device attributes in sysfs 2892082a88SKen Xue * ACPI_APD_PM : attach power domain to device 2992082a88SKen Xue */ 3092082a88SKen Xue #define ACPI_APD_SYSFS BIT(0) 3192082a88SKen Xue #define ACPI_APD_PM BIT(1) 3292082a88SKen Xue 3392082a88SKen Xue /** 3492082a88SKen Xue * struct apd_device_desc - a descriptor for apd device 3592082a88SKen Xue * @flags: device flags like %ACPI_APD_SYSFS, %ACPI_APD_PM 3692082a88SKen Xue * @fixed_clk_rate: fixed rate input clock source for acpi device; 3792082a88SKen Xue * 0 means no fixed rate input clock source 3892082a88SKen Xue * @setup: a hook routine to set device resource during create platform device 3992082a88SKen Xue * 4092082a88SKen Xue * Device description defined as acpi_device_id.driver_data 4192082a88SKen Xue */ 4292082a88SKen Xue struct apd_device_desc { 4392082a88SKen Xue unsigned int flags; 4492082a88SKen Xue unsigned int fixed_clk_rate; 457ff55d17SHeikki Krogerus struct property_entry *properties; 4692082a88SKen Xue int (*setup)(struct apd_private_data *pdata); 4792082a88SKen Xue }; 4892082a88SKen Xue 4992082a88SKen Xue struct apd_private_data { 5092082a88SKen Xue struct clk *clk; 5192082a88SKen Xue struct acpi_device *adev; 5292082a88SKen Xue const struct apd_device_desc *dev_desc; 5392082a88SKen Xue }; 5492082a88SKen Xue 55b790eb20SLoc Ho #if defined(CONFIG_X86_AMD_PLATFORM_DEVICE) || defined(CONFIG_ARM64) 5692082a88SKen Xue #define APD_ADDR(desc) ((unsigned long)&desc) 5792082a88SKen Xue 5892082a88SKen Xue static int acpi_apd_setup(struct apd_private_data *pdata) 5992082a88SKen Xue { 6092082a88SKen Xue const struct apd_device_desc *dev_desc = pdata->dev_desc; 6192082a88SKen Xue struct clk *clk = ERR_PTR(-ENODEV); 6292082a88SKen Xue 6392082a88SKen Xue if (dev_desc->fixed_clk_rate) { 6492082a88SKen Xue clk = clk_register_fixed_rate(&pdata->adev->dev, 6592082a88SKen Xue dev_name(&pdata->adev->dev), 66cdd9a6b2SStephen Boyd NULL, 0, dev_desc->fixed_clk_rate); 6792082a88SKen Xue clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev)); 6892082a88SKen Xue pdata->clk = clk; 6992082a88SKen Xue } 7092082a88SKen Xue 7192082a88SKen Xue return 0; 7292082a88SKen Xue } 7392082a88SKen Xue 74b790eb20SLoc Ho #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE 7592082a88SKen Xue static struct apd_device_desc cz_i2c_desc = { 7692082a88SKen Xue .setup = acpi_apd_setup, 7792082a88SKen Xue .fixed_clk_rate = 133000000, 7892082a88SKen Xue }; 7992082a88SKen Xue 807ff55d17SHeikki Krogerus static struct property_entry uart_properties[] = { 817ff55d17SHeikki Krogerus PROPERTY_ENTRY_U32("reg-io-width", 4), 827ff55d17SHeikki Krogerus PROPERTY_ENTRY_U32("reg-shift", 2), 837ff55d17SHeikki Krogerus PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), 847ff55d17SHeikki Krogerus { }, 857ff55d17SHeikki Krogerus }; 867ff55d17SHeikki Krogerus 8792082a88SKen Xue static struct apd_device_desc cz_uart_desc = { 8892082a88SKen Xue .setup = acpi_apd_setup, 8992082a88SKen Xue .fixed_clk_rate = 48000000, 907ff55d17SHeikki Krogerus .properties = uart_properties, 9192082a88SKen Xue }; 92b790eb20SLoc Ho #endif 93b790eb20SLoc Ho 94b790eb20SLoc Ho #ifdef CONFIG_ARM64 95b790eb20SLoc Ho static struct apd_device_desc xgene_i2c_desc = { 96b790eb20SLoc Ho .setup = acpi_apd_setup, 97b790eb20SLoc Ho .fixed_clk_rate = 100000000, 98b790eb20SLoc Ho }; 99b790eb20SLoc Ho #endif 10092082a88SKen Xue 10192082a88SKen Xue #else 10292082a88SKen Xue 10392082a88SKen Xue #define APD_ADDR(desc) (0UL) 10492082a88SKen Xue 10592082a88SKen Xue #endif /* CONFIG_X86_AMD_PLATFORM_DEVICE */ 10692082a88SKen Xue 10792082a88SKen Xue /** 10892082a88SKen Xue * Create platform device during acpi scan attach handle. 10992082a88SKen Xue * Return value > 0 on success of creating device. 11092082a88SKen Xue */ 11192082a88SKen Xue static int acpi_apd_create_device(struct acpi_device *adev, 11292082a88SKen Xue const struct acpi_device_id *id) 11392082a88SKen Xue { 11492082a88SKen Xue const struct apd_device_desc *dev_desc = (void *)id->driver_data; 11592082a88SKen Xue struct apd_private_data *pdata; 11692082a88SKen Xue struct platform_device *pdev; 11792082a88SKen Xue int ret; 11892082a88SKen Xue 11992082a88SKen Xue if (!dev_desc) { 120*1571875bSHeikki Krogerus pdev = acpi_create_platform_device(adev, NULL); 12192082a88SKen Xue return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; 12292082a88SKen Xue } 12392082a88SKen Xue 12492082a88SKen Xue pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 12592082a88SKen Xue if (!pdata) 12692082a88SKen Xue return -ENOMEM; 12792082a88SKen Xue 12892082a88SKen Xue pdata->adev = adev; 12992082a88SKen Xue pdata->dev_desc = dev_desc; 13092082a88SKen Xue 13192082a88SKen Xue if (dev_desc->setup) { 13292082a88SKen Xue ret = dev_desc->setup(pdata); 13392082a88SKen Xue if (ret) 13492082a88SKen Xue goto err_out; 13592082a88SKen Xue } 13692082a88SKen Xue 13792082a88SKen Xue adev->driver_data = pdata; 138*1571875bSHeikki Krogerus pdev = acpi_create_platform_device(adev, dev_desc->properties); 13992082a88SKen Xue if (!IS_ERR_OR_NULL(pdev)) 14092082a88SKen Xue return 1; 14192082a88SKen Xue 14292082a88SKen Xue ret = PTR_ERR(pdev); 14392082a88SKen Xue adev->driver_data = NULL; 14492082a88SKen Xue 14592082a88SKen Xue err_out: 14692082a88SKen Xue kfree(pdata); 14792082a88SKen Xue return ret; 14892082a88SKen Xue } 14992082a88SKen Xue 15092082a88SKen Xue static const struct acpi_device_id acpi_apd_device_ids[] = { 15192082a88SKen Xue /* Generic apd devices */ 152b790eb20SLoc Ho #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE 15392082a88SKen Xue { "AMD0010", APD_ADDR(cz_i2c_desc) }, 154e4e666baSXiangliang Yu { "AMDI0010", APD_ADDR(cz_i2c_desc) }, 15592082a88SKen Xue { "AMD0020", APD_ADDR(cz_uart_desc) }, 156f5eda99eSWang Hongcheng { "AMDI0020", APD_ADDR(cz_uart_desc) }, 15792082a88SKen Xue { "AMD0030", }, 158b790eb20SLoc Ho #endif 159b790eb20SLoc Ho #ifdef CONFIG_ARM64 160b790eb20SLoc Ho { "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, 161b790eb20SLoc Ho #endif 16292082a88SKen Xue { } 16392082a88SKen Xue }; 16492082a88SKen Xue 16592082a88SKen Xue static struct acpi_scan_handler apd_handler = { 16692082a88SKen Xue .ids = acpi_apd_device_ids, 16792082a88SKen Xue .attach = acpi_apd_create_device, 16892082a88SKen Xue }; 16992082a88SKen Xue 17092082a88SKen Xue void __init acpi_apd_init(void) 17192082a88SKen Xue { 17292082a88SKen Xue acpi_scan_add_handler(&apd_handler); 17392082a88SKen Xue } 174