1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 292082a88SKen Xue /* 392082a88SKen Xue * AMD ACPI support for ACPI2platform device. 492082a88SKen Xue * 592082a88SKen Xue * Copyright (c) 2014,2015 AMD Corporation. 692082a88SKen Xue * Authors: Ken Xue <Ken.Xue@amd.com> 792082a88SKen Xue * Wu, Jeff <Jeff.Wu@amd.com> 892082a88SKen Xue */ 992082a88SKen Xue 1092082a88SKen Xue #include <linux/acpi.h> 1132c6f3ffSHanjun Guo #include <linux/clkdev.h> 1232c6f3ffSHanjun Guo #include <linux/clk-provider.h> 1392082a88SKen Xue #include <linux/err.h> 1462e59c4eSStephen Boyd #include <linux/io.h> 1532c6f3ffSHanjun Guo #include <linux/platform_data/clk-fch.h> 1632c6f3ffSHanjun Guo #include <linux/platform_device.h> 1792082a88SKen Xue 1892082a88SKen Xue #include "internal.h" 1992082a88SKen Xue 2092082a88SKen Xue struct apd_private_data; 2192082a88SKen Xue 2292082a88SKen Xue /** 2392082a88SKen Xue * struct apd_device_desc - a descriptor for apd device 2492082a88SKen Xue * @fixed_clk_rate: fixed rate input clock source for acpi device; 2592082a88SKen Xue * 0 means no fixed rate input clock source 26ad4a0f24SHanjun Guo * @properties: build-in properties of the device such as UART 2792082a88SKen Xue * @setup: a hook routine to set device resource during create platform device 2892082a88SKen Xue * 2992082a88SKen Xue * Device description defined as acpi_device_id.driver_data 3092082a88SKen Xue */ 3192082a88SKen Xue struct apd_device_desc { 3292082a88SKen Xue unsigned int fixed_clk_rate; 337ff55d17SHeikki Krogerus struct property_entry *properties; 3492082a88SKen Xue int (*setup)(struct apd_private_data *pdata); 3592082a88SKen Xue }; 3692082a88SKen Xue 3792082a88SKen Xue struct apd_private_data { 3892082a88SKen Xue struct clk *clk; 3992082a88SKen Xue struct acpi_device *adev; 4092082a88SKen Xue const struct apd_device_desc *dev_desc; 4192082a88SKen Xue }; 4292082a88SKen Xue 43b790eb20SLoc Ho #if defined(CONFIG_X86_AMD_PLATFORM_DEVICE) || defined(CONFIG_ARM64) 4492082a88SKen Xue #define APD_ADDR(desc) ((unsigned long)&desc) 4592082a88SKen Xue 4692082a88SKen Xue static int acpi_apd_setup(struct apd_private_data *pdata) 4792082a88SKen Xue { 4892082a88SKen Xue const struct apd_device_desc *dev_desc = pdata->dev_desc; 49b244883cSColin Ian King struct clk *clk; 5092082a88SKen Xue 5192082a88SKen Xue if (dev_desc->fixed_clk_rate) { 5292082a88SKen Xue clk = clk_register_fixed_rate(&pdata->adev->dev, 5392082a88SKen Xue dev_name(&pdata->adev->dev), 54cdd9a6b2SStephen Boyd NULL, 0, dev_desc->fixed_clk_rate); 5592082a88SKen Xue clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev)); 5692082a88SKen Xue pdata->clk = clk; 5792082a88SKen Xue } 5892082a88SKen Xue 5992082a88SKen Xue return 0; 6092082a88SKen Xue } 6192082a88SKen Xue 62b790eb20SLoc Ho #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE 633f4ba94eSAkshu Agrawal 64d58669b0SAkshu Agrawal static int fch_misc_setup(struct apd_private_data *pdata) 653f4ba94eSAkshu Agrawal { 663f4ba94eSAkshu Agrawal struct acpi_device *adev = pdata->adev; 677f8802f2SAkshu Agrawal const union acpi_object *obj; 683f4ba94eSAkshu Agrawal struct platform_device *clkdev; 69d58669b0SAkshu Agrawal struct fch_clk_data *clk_data; 703f4ba94eSAkshu Agrawal struct resource_entry *rentry; 713f4ba94eSAkshu Agrawal struct list_head resource_list; 723f4ba94eSAkshu Agrawal int ret; 733f4ba94eSAkshu Agrawal 743f4ba94eSAkshu Agrawal clk_data = devm_kzalloc(&adev->dev, sizeof(*clk_data), GFP_KERNEL); 753f4ba94eSAkshu Agrawal if (!clk_data) 763f4ba94eSAkshu Agrawal return -ENOMEM; 773f4ba94eSAkshu Agrawal 783f4ba94eSAkshu Agrawal INIT_LIST_HEAD(&resource_list); 79064d42d9SHeikki Krogerus ret = acpi_dev_get_memory_resources(adev, &resource_list); 803f4ba94eSAkshu Agrawal if (ret < 0) 813f4ba94eSAkshu Agrawal return -ENOENT; 823f4ba94eSAkshu Agrawal 837fdb98e8SAjit Kumar Pandey if (!acpi_dev_get_property(adev, "clk-name", ACPI_TYPE_STRING, &obj)) { 847fdb98e8SAjit Kumar Pandey clk_data->name = devm_kzalloc(&adev->dev, obj->string.length, 857fdb98e8SAjit Kumar Pandey GFP_KERNEL); 86*4dea4177SKang Chen if (!clk_data->name) 87*4dea4177SKang Chen return -ENOMEM; 887fdb98e8SAjit Kumar Pandey 897fdb98e8SAjit Kumar Pandey strcpy(clk_data->name, obj->string.pointer); 907fdb98e8SAjit Kumar Pandey } else { 917fdb98e8SAjit Kumar Pandey /* Set default name to mclk if entry missing in firmware */ 927fdb98e8SAjit Kumar Pandey clk_data->name = "mclk"; 937fdb98e8SAjit Kumar Pandey } 947fdb98e8SAjit Kumar Pandey 953f4ba94eSAkshu Agrawal list_for_each_entry(rentry, &resource_list, node) { 963f4ba94eSAkshu Agrawal clk_data->base = devm_ioremap(&adev->dev, rentry->res->start, 973f4ba94eSAkshu Agrawal resource_size(rentry->res)); 983f4ba94eSAkshu Agrawal break; 993f4ba94eSAkshu Agrawal } 1002cea3ec5SJiasheng Jiang if (!clk_data->base) 1012cea3ec5SJiasheng Jiang return -ENOMEM; 1023f4ba94eSAkshu Agrawal 1033f4ba94eSAkshu Agrawal acpi_dev_free_resource_list(&resource_list); 1043f4ba94eSAkshu Agrawal 105d58669b0SAkshu Agrawal clkdev = platform_device_register_data(&adev->dev, "clk-fch", 1063f4ba94eSAkshu Agrawal PLATFORM_DEVID_NONE, clk_data, 1073f4ba94eSAkshu Agrawal sizeof(*clk_data)); 1083f4ba94eSAkshu Agrawal return PTR_ERR_OR_ZERO(clkdev); 1093f4ba94eSAkshu Agrawal } 1103f4ba94eSAkshu Agrawal 111a217726aSJulia Lawall static const struct apd_device_desc cz_i2c_desc = { 11292082a88SKen Xue .setup = acpi_apd_setup, 11392082a88SKen Xue .fixed_clk_rate = 133000000, 11492082a88SKen Xue }; 11592082a88SKen Xue 1164881f0baSNehal Shah static const struct apd_device_desc wt_i2c_desc = { 1174881f0baSNehal Shah .setup = acpi_apd_setup, 1184881f0baSNehal Shah .fixed_clk_rate = 150000000, 1194881f0baSNehal Shah }; 1204881f0baSNehal Shah 1217ff55d17SHeikki Krogerus static struct property_entry uart_properties[] = { 1227ff55d17SHeikki Krogerus PROPERTY_ENTRY_U32("reg-io-width", 4), 1237ff55d17SHeikki Krogerus PROPERTY_ENTRY_U32("reg-shift", 2), 1247ff55d17SHeikki Krogerus PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), 1257ff55d17SHeikki Krogerus { }, 1267ff55d17SHeikki Krogerus }; 1277ff55d17SHeikki Krogerus 128a217726aSJulia Lawall static const struct apd_device_desc cz_uart_desc = { 12992082a88SKen Xue .setup = acpi_apd_setup, 13092082a88SKen Xue .fixed_clk_rate = 48000000, 1317ff55d17SHeikki Krogerus .properties = uart_properties, 13292082a88SKen Xue }; 1333f4ba94eSAkshu Agrawal 134d58669b0SAkshu Agrawal static const struct apd_device_desc fch_misc_desc = { 135d58669b0SAkshu Agrawal .setup = fch_misc_setup, 1363f4ba94eSAkshu Agrawal }; 137ee2bc5d2SHanjun Guo #endif /* CONFIG_X86_AMD_PLATFORM_DEVICE */ 138b790eb20SLoc Ho 139b790eb20SLoc Ho #ifdef CONFIG_ARM64 140a217726aSJulia Lawall static const struct apd_device_desc xgene_i2c_desc = { 141b790eb20SLoc Ho .setup = acpi_apd_setup, 142b790eb20SLoc Ho .fixed_clk_rate = 100000000, 143b790eb20SLoc Ho }; 144bd2058dcSKamlakant Patel 145a217726aSJulia Lawall static const struct apd_device_desc vulcan_spi_desc = { 146bd2058dcSKamlakant Patel .setup = acpi_apd_setup, 147bd2058dcSKamlakant Patel .fixed_clk_rate = 133000000, 148bd2058dcSKamlakant Patel }; 1496e14cf36SHanjun Guo 1506e14cf36SHanjun Guo static const struct apd_device_desc hip07_i2c_desc = { 1516e14cf36SHanjun Guo .setup = acpi_apd_setup, 1526e14cf36SHanjun Guo .fixed_clk_rate = 200000000, 1536e14cf36SHanjun Guo }; 1546e14cf36SHanjun Guo 1556e14cf36SHanjun Guo static const struct apd_device_desc hip08_i2c_desc = { 1566e14cf36SHanjun Guo .setup = acpi_apd_setup, 1576e14cf36SHanjun Guo .fixed_clk_rate = 250000000, 1586e14cf36SHanjun Guo }; 15911330a9fSChuanhua Han 160c01a4a13SHanjun Guo static const struct apd_device_desc hip08_lite_i2c_desc = { 161c01a4a13SHanjun Guo .setup = acpi_apd_setup, 162c01a4a13SHanjun Guo .fixed_clk_rate = 125000000, 163c01a4a13SHanjun Guo }; 164c01a4a13SHanjun Guo 1651977dbefSJayachandran C static const struct apd_device_desc thunderx2_i2c_desc = { 1661977dbefSJayachandran C .setup = acpi_apd_setup, 1671977dbefSJayachandran C .fixed_clk_rate = 125000000, 1681977dbefSJayachandran C }; 16956131d6dSJay Fang 17011330a9fSChuanhua Han static const struct apd_device_desc nxp_i2c_desc = { 17111330a9fSChuanhua Han .setup = acpi_apd_setup, 17211330a9fSChuanhua Han .fixed_clk_rate = 350000000, 17311330a9fSChuanhua Han }; 17411330a9fSChuanhua Han 17556131d6dSJay Fang static const struct apd_device_desc hip08_spi_desc = { 17656131d6dSJay Fang .setup = acpi_apd_setup, 17756131d6dSJay Fang .fixed_clk_rate = 250000000, 17856131d6dSJay Fang }; 179ee2bc5d2SHanjun Guo #endif /* CONFIG_ARM64 */ 180ee2bc5d2SHanjun Guo 181b790eb20SLoc Ho #endif 18292082a88SKen Xue 1830955b3a5SXiaofei Tan /* 18492082a88SKen Xue * Create platform device during acpi scan attach handle. 18592082a88SKen Xue * Return value > 0 on success of creating device. 18692082a88SKen Xue */ 18792082a88SKen Xue static int acpi_apd_create_device(struct acpi_device *adev, 18892082a88SKen Xue const struct acpi_device_id *id) 18992082a88SKen Xue { 19092082a88SKen Xue const struct apd_device_desc *dev_desc = (void *)id->driver_data; 19192082a88SKen Xue struct apd_private_data *pdata; 19292082a88SKen Xue struct platform_device *pdev; 19392082a88SKen Xue int ret; 19492082a88SKen Xue 19592082a88SKen Xue if (!dev_desc) { 1961571875bSHeikki Krogerus pdev = acpi_create_platform_device(adev, NULL); 19792082a88SKen Xue return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; 19892082a88SKen Xue } 19992082a88SKen Xue 20092082a88SKen Xue pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 20192082a88SKen Xue if (!pdata) 20292082a88SKen Xue return -ENOMEM; 20392082a88SKen Xue 20492082a88SKen Xue pdata->adev = adev; 20592082a88SKen Xue pdata->dev_desc = dev_desc; 20692082a88SKen Xue 20792082a88SKen Xue if (dev_desc->setup) { 20892082a88SKen Xue ret = dev_desc->setup(pdata); 20992082a88SKen Xue if (ret) 21092082a88SKen Xue goto err_out; 21192082a88SKen Xue } 21292082a88SKen Xue 21392082a88SKen Xue adev->driver_data = pdata; 2141571875bSHeikki Krogerus pdev = acpi_create_platform_device(adev, dev_desc->properties); 21592082a88SKen Xue if (!IS_ERR_OR_NULL(pdev)) 21692082a88SKen Xue return 1; 21792082a88SKen Xue 21892082a88SKen Xue ret = PTR_ERR(pdev); 21992082a88SKen Xue adev->driver_data = NULL; 22092082a88SKen Xue 22192082a88SKen Xue err_out: 22292082a88SKen Xue kfree(pdata); 22392082a88SKen Xue return ret; 22492082a88SKen Xue } 22592082a88SKen Xue 22692082a88SKen Xue static const struct acpi_device_id acpi_apd_device_ids[] = { 22792082a88SKen Xue /* Generic apd devices */ 228b790eb20SLoc Ho #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE 22992082a88SKen Xue { "AMD0010", APD_ADDR(cz_i2c_desc) }, 23092082a88SKen Xue { "AMD0020", APD_ADDR(cz_uart_desc) }, 23192082a88SKen Xue { "AMD0030", }, 232d58669b0SAkshu Agrawal { "AMD0040", APD_ADDR(fch_misc_desc)}, 23378d5e9e2SJan Dabros { "AMDI0010", APD_ADDR(wt_i2c_desc) }, 23478d5e9e2SJan Dabros { "AMDI0019", APD_ADDR(wt_i2c_desc) }, 23578d5e9e2SJan Dabros { "AMDI0020", APD_ADDR(cz_uart_desc) }, 23678d5e9e2SJan Dabros { "AMDI0022", APD_ADDR(cz_uart_desc) }, 237384b02d6SPu Wen { "HYGO0010", APD_ADDR(wt_i2c_desc) }, 238b790eb20SLoc Ho #endif 239b790eb20SLoc Ho #ifdef CONFIG_ARM64 240b790eb20SLoc Ho { "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, 241bd2058dcSKamlakant Patel { "BRCM900D", APD_ADDR(vulcan_spi_desc) }, 242251831bdSJayachandran C { "CAV900D", APD_ADDR(vulcan_spi_desc) }, 2431977dbefSJayachandran C { "CAV9007", APD_ADDR(thunderx2_i2c_desc) }, 244f7f3dd5bSHanjun Guo { "HISI02A1", APD_ADDR(hip07_i2c_desc) }, 245f7f3dd5bSHanjun Guo { "HISI02A2", APD_ADDR(hip08_i2c_desc) }, 246c01a4a13SHanjun Guo { "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) }, 24756131d6dSJay Fang { "HISI0173", APD_ADDR(hip08_spi_desc) }, 24811330a9fSChuanhua Han { "NXP0001", APD_ADDR(nxp_i2c_desc) }, 249b790eb20SLoc Ho #endif 25092082a88SKen Xue { } 25192082a88SKen Xue }; 25292082a88SKen Xue 25392082a88SKen Xue static struct acpi_scan_handler apd_handler = { 25492082a88SKen Xue .ids = acpi_apd_device_ids, 25592082a88SKen Xue .attach = acpi_apd_create_device, 25692082a88SKen Xue }; 25792082a88SKen Xue 25892082a88SKen Xue void __init acpi_apd_init(void) 25992082a88SKen Xue { 26092082a88SKen Xue acpi_scan_add_handler(&apd_handler); 26192082a88SKen Xue } 262