acpi_apd.c 4.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/*
 * AMD ACPI support for ACPI2platform device.
 *
 * Copyright (c) 2014,2015 AMD Corporation.
 * Authors: Ken Xue <Ken.Xue@amd.com>
 *	Wu, Jeff <Jeff.Wu@amd.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/clkdev.h>
#include <linux/acpi.h>
#include <linux/err.h>
#include <linux/pm.h>

#include "internal.h"

ACPI_MODULE_NAME("acpi_apd");
struct apd_private_data;

/**
 * ACPI_APD_SYSFS : add device attributes in sysfs
 * ACPI_APD_PM : attach power domain to device
 */
#define ACPI_APD_SYSFS	BIT(0)
#define ACPI_APD_PM	BIT(1)

/**
 * struct apd_device_desc - a descriptor for apd device
 * @flags: device flags like %ACPI_APD_SYSFS, %ACPI_APD_PM
 * @fixed_clk_rate: fixed rate input clock source for acpi device;
 *			0 means no fixed rate input clock source
 * @setup: a hook routine to set device resource during create platform device
 *
 * Device description defined as acpi_device_id.driver_data
 */
struct apd_device_desc {
	unsigned int flags;
	unsigned int fixed_clk_rate;
45
	struct property_entry *properties;
46 47 48 49 50 51 52 53 54
	int (*setup)(struct apd_private_data *pdata);
};

struct apd_private_data {
	struct clk *clk;
	struct acpi_device *adev;
	const struct apd_device_desc *dev_desc;
};

55
#if defined(CONFIG_X86_AMD_PLATFORM_DEVICE) || defined(CONFIG_ARM64)
56 57 58 59 60 61 62 63 64 65
#define APD_ADDR(desc)	((unsigned long)&desc)

static int acpi_apd_setup(struct apd_private_data *pdata)
{
	const struct apd_device_desc *dev_desc = pdata->dev_desc;
	struct clk *clk = ERR_PTR(-ENODEV);

	if (dev_desc->fixed_clk_rate) {
		clk = clk_register_fixed_rate(&pdata->adev->dev,
					dev_name(&pdata->adev->dev),
S
Stephen Boyd 已提交
66
					NULL, 0, dev_desc->fixed_clk_rate);
67 68 69 70 71 72 73
		clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev));
		pdata->clk = clk;
	}

	return 0;
}

74
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
75
static const struct apd_device_desc cz_i2c_desc = {
76 77 78 79
	.setup = acpi_apd_setup,
	.fixed_clk_rate = 133000000,
};

80 81 82 83 84 85 86
static struct property_entry uart_properties[] = {
	PROPERTY_ENTRY_U32("reg-io-width", 4),
	PROPERTY_ENTRY_U32("reg-shift", 2),
	PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
	{ },
};

87
static const struct apd_device_desc cz_uart_desc = {
88 89
	.setup = acpi_apd_setup,
	.fixed_clk_rate = 48000000,
90
	.properties = uart_properties,
91
};
92 93 94
#endif

#ifdef CONFIG_ARM64
95
static const struct apd_device_desc xgene_i2c_desc = {
96 97 98
	.setup = acpi_apd_setup,
	.fixed_clk_rate = 100000000,
};
99

100
static const struct apd_device_desc vulcan_spi_desc = {
101 102 103
	.setup = acpi_apd_setup,
	.fixed_clk_rate = 133000000,
};
104
#endif
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

#else

#define APD_ADDR(desc) (0UL)

#endif /* CONFIG_X86_AMD_PLATFORM_DEVICE */

/**
* Create platform device during acpi scan attach handle.
* Return value > 0 on success of creating device.
*/
static int acpi_apd_create_device(struct acpi_device *adev,
				   const struct acpi_device_id *id)
{
	const struct apd_device_desc *dev_desc = (void *)id->driver_data;
	struct apd_private_data *pdata;
	struct platform_device *pdev;
	int ret;

	if (!dev_desc) {
		pdev = acpi_create_platform_device(adev);
		return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1;
	}

	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	pdata->adev = adev;
	pdata->dev_desc = dev_desc;

	if (dev_desc->setup) {
		ret = dev_desc->setup(pdata);
		if (ret)
			goto err_out;
	}

142 143 144 145 146 147
	if (dev_desc->properties) {
		ret = device_add_properties(&adev->dev, dev_desc->properties);
		if (ret)
			goto err_out;
	}

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
	adev->driver_data = pdata;
	pdev = acpi_create_platform_device(adev);
	if (!IS_ERR_OR_NULL(pdev))
		return 1;

	ret = PTR_ERR(pdev);
	adev->driver_data = NULL;

 err_out:
	kfree(pdata);
	return ret;
}

static const struct acpi_device_id acpi_apd_device_ids[] = {
	/* Generic apd devices */
163
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
164
	{ "AMD0010", APD_ADDR(cz_i2c_desc) },
165
	{ "AMDI0010", APD_ADDR(cz_i2c_desc) },
166
	{ "AMD0020", APD_ADDR(cz_uart_desc) },
167
	{ "AMDI0020", APD_ADDR(cz_uart_desc) },
168
	{ "AMD0030", },
169 170 171
#endif
#ifdef CONFIG_ARM64
	{ "APMC0D0F", APD_ADDR(xgene_i2c_desc) },
172
	{ "BRCM900D", APD_ADDR(vulcan_spi_desc) },
173
#endif
174 175 176 177 178 179 180 181 182 183 184 185
	{ }
};

static struct acpi_scan_handler apd_handler = {
	.ids = acpi_apd_device_ids,
	.attach = acpi_apd_create_device,
};

void __init acpi_apd_init(void)
{
	acpi_scan_add_handler(&apd_handler);
}