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> 14*3f4ba94eSAkshu Agrawal #include <linux/platform_data/clk-st.h> 1592082a88SKen Xue #include <linux/platform_device.h> 1692082a88SKen Xue #include <linux/pm_domain.h> 1792082a88SKen Xue #include <linux/clkdev.h> 1892082a88SKen Xue #include <linux/acpi.h> 1992082a88SKen Xue #include <linux/err.h> 2092082a88SKen Xue #include <linux/pm.h> 2192082a88SKen Xue 2292082a88SKen Xue #include "internal.h" 2392082a88SKen Xue 2492082a88SKen Xue ACPI_MODULE_NAME("acpi_apd"); 2592082a88SKen Xue struct apd_private_data; 2692082a88SKen Xue 2792082a88SKen Xue /** 2892082a88SKen Xue * ACPI_APD_SYSFS : add device attributes in sysfs 2992082a88SKen Xue * ACPI_APD_PM : attach power domain to device 3092082a88SKen Xue */ 3192082a88SKen Xue #define ACPI_APD_SYSFS BIT(0) 3292082a88SKen Xue #define ACPI_APD_PM BIT(1) 3392082a88SKen Xue 3492082a88SKen Xue /** 3592082a88SKen Xue * struct apd_device_desc - a descriptor for apd device 3692082a88SKen Xue * @flags: device flags like %ACPI_APD_SYSFS, %ACPI_APD_PM 3792082a88SKen Xue * @fixed_clk_rate: fixed rate input clock source for acpi device; 3892082a88SKen Xue * 0 means no fixed rate input clock source 3992082a88SKen Xue * @setup: a hook routine to set device resource during create platform device 4092082a88SKen Xue * 4192082a88SKen Xue * Device description defined as acpi_device_id.driver_data 4292082a88SKen Xue */ 4392082a88SKen Xue struct apd_device_desc { 4492082a88SKen Xue unsigned int flags; 4592082a88SKen Xue unsigned int fixed_clk_rate; 467ff55d17SHeikki Krogerus struct property_entry *properties; 4792082a88SKen Xue int (*setup)(struct apd_private_data *pdata); 4892082a88SKen Xue }; 4992082a88SKen Xue 5092082a88SKen Xue struct apd_private_data { 5192082a88SKen Xue struct clk *clk; 5292082a88SKen Xue struct acpi_device *adev; 5392082a88SKen Xue const struct apd_device_desc *dev_desc; 5492082a88SKen Xue }; 5592082a88SKen Xue 56b790eb20SLoc Ho #if defined(CONFIG_X86_AMD_PLATFORM_DEVICE) || defined(CONFIG_ARM64) 5792082a88SKen Xue #define APD_ADDR(desc) ((unsigned long)&desc) 5892082a88SKen Xue 5992082a88SKen Xue static int acpi_apd_setup(struct apd_private_data *pdata) 6092082a88SKen Xue { 6192082a88SKen Xue const struct apd_device_desc *dev_desc = pdata->dev_desc; 6292082a88SKen Xue struct clk *clk = ERR_PTR(-ENODEV); 6392082a88SKen Xue 6492082a88SKen Xue if (dev_desc->fixed_clk_rate) { 6592082a88SKen Xue clk = clk_register_fixed_rate(&pdata->adev->dev, 6692082a88SKen Xue dev_name(&pdata->adev->dev), 67cdd9a6b2SStephen Boyd NULL, 0, dev_desc->fixed_clk_rate); 6892082a88SKen Xue clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev)); 6992082a88SKen Xue pdata->clk = clk; 7092082a88SKen Xue } 7192082a88SKen Xue 7292082a88SKen Xue return 0; 7392082a88SKen Xue } 7492082a88SKen Xue 75b790eb20SLoc Ho #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE 76*3f4ba94eSAkshu Agrawal 77*3f4ba94eSAkshu Agrawal static int misc_check_res(struct acpi_resource *ares, void *data) 78*3f4ba94eSAkshu Agrawal { 79*3f4ba94eSAkshu Agrawal struct resource res; 80*3f4ba94eSAkshu Agrawal 81*3f4ba94eSAkshu Agrawal return !acpi_dev_resource_memory(ares, &res); 82*3f4ba94eSAkshu Agrawal } 83*3f4ba94eSAkshu Agrawal 84*3f4ba94eSAkshu Agrawal static int st_misc_setup(struct apd_private_data *pdata) 85*3f4ba94eSAkshu Agrawal { 86*3f4ba94eSAkshu Agrawal struct acpi_device *adev = pdata->adev; 87*3f4ba94eSAkshu Agrawal struct platform_device *clkdev; 88*3f4ba94eSAkshu Agrawal struct st_clk_data *clk_data; 89*3f4ba94eSAkshu Agrawal struct resource_entry *rentry; 90*3f4ba94eSAkshu Agrawal struct list_head resource_list; 91*3f4ba94eSAkshu Agrawal int ret; 92*3f4ba94eSAkshu Agrawal 93*3f4ba94eSAkshu Agrawal clk_data = devm_kzalloc(&adev->dev, sizeof(*clk_data), GFP_KERNEL); 94*3f4ba94eSAkshu Agrawal if (!clk_data) 95*3f4ba94eSAkshu Agrawal return -ENOMEM; 96*3f4ba94eSAkshu Agrawal 97*3f4ba94eSAkshu Agrawal INIT_LIST_HEAD(&resource_list); 98*3f4ba94eSAkshu Agrawal ret = acpi_dev_get_resources(adev, &resource_list, misc_check_res, 99*3f4ba94eSAkshu Agrawal NULL); 100*3f4ba94eSAkshu Agrawal if (ret < 0) 101*3f4ba94eSAkshu Agrawal return -ENOENT; 102*3f4ba94eSAkshu Agrawal 103*3f4ba94eSAkshu Agrawal list_for_each_entry(rentry, &resource_list, node) { 104*3f4ba94eSAkshu Agrawal clk_data->base = devm_ioremap(&adev->dev, rentry->res->start, 105*3f4ba94eSAkshu Agrawal resource_size(rentry->res)); 106*3f4ba94eSAkshu Agrawal break; 107*3f4ba94eSAkshu Agrawal } 108*3f4ba94eSAkshu Agrawal 109*3f4ba94eSAkshu Agrawal acpi_dev_free_resource_list(&resource_list); 110*3f4ba94eSAkshu Agrawal 111*3f4ba94eSAkshu Agrawal clkdev = platform_device_register_data(&adev->dev, "clk-st", 112*3f4ba94eSAkshu Agrawal PLATFORM_DEVID_NONE, clk_data, 113*3f4ba94eSAkshu Agrawal sizeof(*clk_data)); 114*3f4ba94eSAkshu Agrawal return PTR_ERR_OR_ZERO(clkdev); 115*3f4ba94eSAkshu Agrawal } 116*3f4ba94eSAkshu Agrawal 117a217726aSJulia Lawall static const struct apd_device_desc cz_i2c_desc = { 11892082a88SKen Xue .setup = acpi_apd_setup, 11992082a88SKen Xue .fixed_clk_rate = 133000000, 12092082a88SKen Xue }; 12192082a88SKen Xue 1224881f0baSNehal Shah static const struct apd_device_desc wt_i2c_desc = { 1234881f0baSNehal Shah .setup = acpi_apd_setup, 1244881f0baSNehal Shah .fixed_clk_rate = 150000000, 1254881f0baSNehal Shah }; 1264881f0baSNehal Shah 1277ff55d17SHeikki Krogerus static struct property_entry uart_properties[] = { 1287ff55d17SHeikki Krogerus PROPERTY_ENTRY_U32("reg-io-width", 4), 1297ff55d17SHeikki Krogerus PROPERTY_ENTRY_U32("reg-shift", 2), 1307ff55d17SHeikki Krogerus PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), 1317ff55d17SHeikki Krogerus { }, 1327ff55d17SHeikki Krogerus }; 1337ff55d17SHeikki Krogerus 134a217726aSJulia Lawall static const struct apd_device_desc cz_uart_desc = { 13592082a88SKen Xue .setup = acpi_apd_setup, 13692082a88SKen Xue .fixed_clk_rate = 48000000, 1377ff55d17SHeikki Krogerus .properties = uart_properties, 13892082a88SKen Xue }; 139*3f4ba94eSAkshu Agrawal 140*3f4ba94eSAkshu Agrawal static const struct apd_device_desc st_misc_desc = { 141*3f4ba94eSAkshu Agrawal .setup = st_misc_setup, 142*3f4ba94eSAkshu Agrawal }; 143b790eb20SLoc Ho #endif 144b790eb20SLoc Ho 145b790eb20SLoc Ho #ifdef CONFIG_ARM64 146a217726aSJulia Lawall static const struct apd_device_desc xgene_i2c_desc = { 147b790eb20SLoc Ho .setup = acpi_apd_setup, 148b790eb20SLoc Ho .fixed_clk_rate = 100000000, 149b790eb20SLoc Ho }; 150bd2058dcSKamlakant Patel 151a217726aSJulia Lawall static const struct apd_device_desc vulcan_spi_desc = { 152bd2058dcSKamlakant Patel .setup = acpi_apd_setup, 153bd2058dcSKamlakant Patel .fixed_clk_rate = 133000000, 154bd2058dcSKamlakant Patel }; 1556e14cf36SHanjun Guo 1566e14cf36SHanjun Guo static const struct apd_device_desc hip07_i2c_desc = { 1576e14cf36SHanjun Guo .setup = acpi_apd_setup, 1586e14cf36SHanjun Guo .fixed_clk_rate = 200000000, 1596e14cf36SHanjun Guo }; 1606e14cf36SHanjun Guo 1616e14cf36SHanjun Guo static const struct apd_device_desc hip08_i2c_desc = { 1626e14cf36SHanjun Guo .setup = acpi_apd_setup, 1636e14cf36SHanjun Guo .fixed_clk_rate = 250000000, 1646e14cf36SHanjun 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 }; 169b790eb20SLoc Ho #endif 17092082a88SKen Xue 17192082a88SKen Xue #else 17292082a88SKen Xue 17392082a88SKen Xue #define APD_ADDR(desc) (0UL) 17492082a88SKen Xue 17592082a88SKen Xue #endif /* CONFIG_X86_AMD_PLATFORM_DEVICE */ 17692082a88SKen Xue 17792082a88SKen Xue /** 17892082a88SKen Xue * Create platform device during acpi scan attach handle. 17992082a88SKen Xue * Return value > 0 on success of creating device. 18092082a88SKen Xue */ 18192082a88SKen Xue static int acpi_apd_create_device(struct acpi_device *adev, 18292082a88SKen Xue const struct acpi_device_id *id) 18392082a88SKen Xue { 18492082a88SKen Xue const struct apd_device_desc *dev_desc = (void *)id->driver_data; 18592082a88SKen Xue struct apd_private_data *pdata; 18692082a88SKen Xue struct platform_device *pdev; 18792082a88SKen Xue int ret; 18892082a88SKen Xue 18992082a88SKen Xue if (!dev_desc) { 1901571875bSHeikki Krogerus pdev = acpi_create_platform_device(adev, NULL); 19192082a88SKen Xue return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; 19292082a88SKen Xue } 19392082a88SKen Xue 19492082a88SKen Xue pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 19592082a88SKen Xue if (!pdata) 19692082a88SKen Xue return -ENOMEM; 19792082a88SKen Xue 19892082a88SKen Xue pdata->adev = adev; 19992082a88SKen Xue pdata->dev_desc = dev_desc; 20092082a88SKen Xue 20192082a88SKen Xue if (dev_desc->setup) { 20292082a88SKen Xue ret = dev_desc->setup(pdata); 20392082a88SKen Xue if (ret) 20492082a88SKen Xue goto err_out; 20592082a88SKen Xue } 20692082a88SKen Xue 20792082a88SKen Xue adev->driver_data = pdata; 2081571875bSHeikki Krogerus pdev = acpi_create_platform_device(adev, dev_desc->properties); 20992082a88SKen Xue if (!IS_ERR_OR_NULL(pdev)) 21092082a88SKen Xue return 1; 21192082a88SKen Xue 21292082a88SKen Xue ret = PTR_ERR(pdev); 21392082a88SKen Xue adev->driver_data = NULL; 21492082a88SKen Xue 21592082a88SKen Xue err_out: 21692082a88SKen Xue kfree(pdata); 21792082a88SKen Xue return ret; 21892082a88SKen Xue } 21992082a88SKen Xue 22092082a88SKen Xue static const struct acpi_device_id acpi_apd_device_ids[] = { 22192082a88SKen Xue /* Generic apd devices */ 222b790eb20SLoc Ho #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE 22392082a88SKen Xue { "AMD0010", APD_ADDR(cz_i2c_desc) }, 2244881f0baSNehal Shah { "AMDI0010", APD_ADDR(wt_i2c_desc) }, 22592082a88SKen Xue { "AMD0020", APD_ADDR(cz_uart_desc) }, 226f5eda99eSWang Hongcheng { "AMDI0020", APD_ADDR(cz_uart_desc) }, 22792082a88SKen Xue { "AMD0030", }, 228*3f4ba94eSAkshu Agrawal { "AMD0040", APD_ADDR(st_misc_desc)}, 229b790eb20SLoc Ho #endif 230b790eb20SLoc Ho #ifdef CONFIG_ARM64 231b790eb20SLoc Ho { "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, 232bd2058dcSKamlakant Patel { "BRCM900D", APD_ADDR(vulcan_spi_desc) }, 233251831bdSJayachandran C { "CAV900D", APD_ADDR(vulcan_spi_desc) }, 2341977dbefSJayachandran C { "CAV9007", APD_ADDR(thunderx2_i2c_desc) }, 235f7f3dd5bSHanjun Guo { "HISI02A1", APD_ADDR(hip07_i2c_desc) }, 236f7f3dd5bSHanjun Guo { "HISI02A2", APD_ADDR(hip08_i2c_desc) }, 237b790eb20SLoc Ho #endif 23892082a88SKen Xue { } 23992082a88SKen Xue }; 24092082a88SKen Xue 24192082a88SKen Xue static struct acpi_scan_handler apd_handler = { 24292082a88SKen Xue .ids = acpi_apd_device_ids, 24392082a88SKen Xue .attach = acpi_apd_create_device, 24492082a88SKen Xue }; 24592082a88SKen Xue 24692082a88SKen Xue void __init acpi_apd_init(void) 24792082a88SKen Xue { 24892082a88SKen Xue acpi_scan_add_handler(&apd_handler); 24992082a88SKen Xue } 250