Path: blob/master/drivers/cpufreq/brcmstb-avs-cpufreq.c
26278 views
/*1* CPU frequency scaling for Broadcom SoCs with AVS firmware that2* supports DVS or DVFS3*4* Copyright (c) 2016 Broadcom5*6* This program is free software; you can redistribute it and/or7* modify it under the terms of the GNU General Public License as8* published by the Free Software Foundation version 2.9*10* This program is distributed "as is" WITHOUT ANY WARRANTY of any11* kind, whether express or implied; without even the implied warranty12* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*/1516/*17* "AVS" is the name of a firmware developed at Broadcom. It derives18* its name from the technique called "Adaptive Voltage Scaling".19* Adaptive voltage scaling was the original purpose of this firmware.20* The AVS firmware still supports "AVS mode", where all it does is21* adaptive voltage scaling. However, on some newer Broadcom SoCs, the22* AVS Firmware, despite its unchanged name, also supports DFS mode and23* DVFS mode.24*25* In the context of this document and the related driver, "AVS" by26* itself always means the Broadcom firmware and never refers to the27* technique called "Adaptive Voltage Scaling".28*29* The Broadcom STB AVS CPUfreq driver provides voltage and frequency30* scaling on Broadcom SoCs using AVS firmware with support for DFS and31* DVFS. The AVS firmware is running on its own co-processor. The32* driver supports both uniprocessor (UP) and symmetric multiprocessor33* (SMP) systems which share clock and voltage across all CPUs.34*35* Actual voltage and frequency scaling is done solely by the AVS36* firmware. This driver does not change frequency or voltage itself.37* It provides a standard CPUfreq interface to the rest of the kernel38* and to userland. It interfaces with the AVS firmware to effect the39* requested changes and to report back the current system status in a40* way that is expected by existing tools.41*/4243#include <linux/cpufreq.h>44#include <linux/delay.h>45#include <linux/interrupt.h>46#include <linux/io.h>47#include <linux/module.h>48#include <linux/of_address.h>49#include <linux/platform_device.h>50#include <linux/semaphore.h>5152/* Max number of arguments AVS calls take */53#define AVS_MAX_CMD_ARGS 454/*55* This macro is used to generate AVS parameter register offsets. For56* x >= AVS_MAX_CMD_ARGS, it returns 0 to protect against accidental memory57* access outside of the parameter range. (Offset 0 is the first parameter.)58*/59#define AVS_PARAM_MULT(x) ((x) < AVS_MAX_CMD_ARGS ? (x) : 0)6061/* AVS Mailbox Register offsets */62#define AVS_MBOX_COMMAND 0x0063#define AVS_MBOX_STATUS 0x0464#define AVS_MBOX_VOLTAGE0 0x0865#define AVS_MBOX_TEMP0 0x0c66#define AVS_MBOX_PV0 0x1067#define AVS_MBOX_MV0 0x1468#define AVS_MBOX_PARAM(x) (0x18 + AVS_PARAM_MULT(x) * sizeof(u32))69#define AVS_MBOX_REVISION 0x2870#define AVS_MBOX_PSTATE 0x2c71#define AVS_MBOX_HEARTBEAT 0x3072#define AVS_MBOX_MAGIC 0x3473#define AVS_MBOX_SIGMA_HVT 0x3874#define AVS_MBOX_SIGMA_SVT 0x3c75#define AVS_MBOX_VOLTAGE1 0x4076#define AVS_MBOX_TEMP1 0x4477#define AVS_MBOX_PV1 0x4878#define AVS_MBOX_MV1 0x4c79#define AVS_MBOX_FREQUENCY 0x508081/* AVS Commands */82#define AVS_CMD_AVAILABLE 0x0083#define AVS_CMD_DISABLE 0x1084#define AVS_CMD_ENABLE 0x1185#define AVS_CMD_S2_ENTER 0x1286#define AVS_CMD_S2_EXIT 0x1387#define AVS_CMD_BBM_ENTER 0x1488#define AVS_CMD_BBM_EXIT 0x1589#define AVS_CMD_S3_ENTER 0x1690#define AVS_CMD_S3_EXIT 0x1791#define AVS_CMD_BALANCE 0x1892/* PMAP and P-STATE commands */93#define AVS_CMD_GET_PMAP 0x3094#define AVS_CMD_SET_PMAP 0x3195#define AVS_CMD_GET_PSTATE 0x4096#define AVS_CMD_SET_PSTATE 0x419798/* Different modes AVS supports (for GET_PMAP/SET_PMAP) */99#define AVS_MODE_AVS 0x0100#define AVS_MODE_DFS 0x1101#define AVS_MODE_DVS 0x2102#define AVS_MODE_DVFS 0x3103104/*105* PMAP parameter p1106* unused:31-24, mdiv_p0:23-16, unused:15-14, pdiv:13-10 , ndiv_int:9-0107*/108#define NDIV_INT_SHIFT 0109#define NDIV_INT_MASK 0x3ff110#define PDIV_SHIFT 10111#define PDIV_MASK 0xf112#define MDIV_P0_SHIFT 16113#define MDIV_P0_MASK 0xff114/*115* PMAP parameter p2116* mdiv_p4:31-24, mdiv_p3:23-16, mdiv_p2:15:8, mdiv_p1:7:0117*/118#define MDIV_P1_SHIFT 0119#define MDIV_P1_MASK 0xff120#define MDIV_P2_SHIFT 8121#define MDIV_P2_MASK 0xff122#define MDIV_P3_SHIFT 16123#define MDIV_P3_MASK 0xff124#define MDIV_P4_SHIFT 24125#define MDIV_P4_MASK 0xff126127/* Different P-STATES AVS supports (for GET_PSTATE/SET_PSTATE) */128#define AVS_PSTATE_P0 0x0129#define AVS_PSTATE_P1 0x1130#define AVS_PSTATE_P2 0x2131#define AVS_PSTATE_P3 0x3132#define AVS_PSTATE_P4 0x4133#define AVS_PSTATE_MAX AVS_PSTATE_P4134135/* CPU L2 Interrupt Controller Registers */136#define AVS_CPU_L2_SET0 0x04137#define AVS_CPU_L2_INT_MASK BIT(31)138139/* AVS Command Status Values */140#define AVS_STATUS_CLEAR 0x00141/* Command/notification accepted */142#define AVS_STATUS_SUCCESS 0xf0143/* Command/notification rejected */144#define AVS_STATUS_FAILURE 0xff145/* Invalid command/notification (unknown) */146#define AVS_STATUS_INVALID 0xf1147/* Non-AVS modes are not supported */148#define AVS_STATUS_NO_SUPP 0xf2149/* Cannot set P-State until P-Map supplied */150#define AVS_STATUS_NO_MAP 0xf3151/* Cannot change P-Map after initial P-Map set */152#define AVS_STATUS_MAP_SET 0xf4153/* Max AVS status; higher numbers are used for debugging */154#define AVS_STATUS_MAX 0xff155156/* Other AVS related constants */157#define AVS_LOOP_LIMIT 10000158#define AVS_TIMEOUT 300 /* in ms; expected completion is < 10ms */159#define AVS_FIRMWARE_MAGIC 0xa11600d1160161#define BRCM_AVS_CPUFREQ_PREFIX "brcmstb-avs"162#define BRCM_AVS_CPUFREQ_NAME BRCM_AVS_CPUFREQ_PREFIX "-cpufreq"163#define BRCM_AVS_CPU_DATA "brcm,avs-cpu-data-mem"164#define BRCM_AVS_CPU_INTR "brcm,avs-cpu-l2-intr"165#define BRCM_AVS_HOST_INTR "sw_intr"166167struct pmap {168unsigned int mode;169unsigned int p1;170unsigned int p2;171unsigned int state;172};173174struct private_data {175void __iomem *base;176void __iomem *avs_intr_base;177struct device *dev;178struct completion done;179struct semaphore sem;180struct pmap pmap;181int host_irq;182};183184static void __iomem *__map_region(const char *name)185{186struct device_node *np;187void __iomem *ptr;188189np = of_find_compatible_node(NULL, NULL, name);190if (!np)191return NULL;192193ptr = of_iomap(np, 0);194of_node_put(np);195196return ptr;197}198199static unsigned long wait_for_avs_command(struct private_data *priv,200unsigned long timeout)201{202unsigned long time_left = 0;203u32 val;204205/* Event driven, wait for the command interrupt */206if (priv->host_irq >= 0)207return wait_for_completion_timeout(&priv->done,208msecs_to_jiffies(timeout));209210/* Polling for command completion */211do {212time_left = timeout;213val = readl(priv->base + AVS_MBOX_STATUS);214if (val)215break;216217usleep_range(1000, 2000);218} while (--timeout);219220return time_left;221}222223static int __issue_avs_command(struct private_data *priv, unsigned int cmd,224unsigned int num_in, unsigned int num_out,225u32 args[])226{227void __iomem *base = priv->base;228unsigned long time_left;229unsigned int i;230int ret;231u32 val;232233ret = down_interruptible(&priv->sem);234if (ret)235return ret;236237/*238* Make sure no other command is currently running: cmd is 0 if AVS239* co-processor is idle. Due to the guard above, we should almost never240* have to wait here.241*/242for (i = 0, val = 1; val != 0 && i < AVS_LOOP_LIMIT; i++)243val = readl(base + AVS_MBOX_COMMAND);244245/* Give the caller a chance to retry if AVS is busy. */246if (i == AVS_LOOP_LIMIT) {247ret = -EAGAIN;248goto out;249}250251/* Clear status before we begin. */252writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);253254/* Provide input parameters */255for (i = 0; i < num_in; i++)256writel(args[i], base + AVS_MBOX_PARAM(i));257258/* Protect from spurious interrupts. */259reinit_completion(&priv->done);260261/* Now issue the command & tell firmware to wake up to process it. */262writel(cmd, base + AVS_MBOX_COMMAND);263writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0);264265/* Wait for AVS co-processor to finish processing the command. */266time_left = wait_for_avs_command(priv, AVS_TIMEOUT);267268/*269* If the AVS status is not in the expected range, it means AVS didn't270* complete our command in time, and we return an error. Also, if there271* is no "time left", we timed out waiting for the interrupt.272*/273val = readl(base + AVS_MBOX_STATUS);274if (time_left == 0 || val == 0 || val > AVS_STATUS_MAX) {275dev_err(priv->dev, "AVS command %#x didn't complete in time\n",276cmd);277dev_err(priv->dev, " Time left: %u ms, AVS status: %#x\n",278jiffies_to_msecs(time_left), val);279ret = -ETIMEDOUT;280goto out;281}282283/* Process returned values */284for (i = 0; i < num_out; i++)285args[i] = readl(base + AVS_MBOX_PARAM(i));286287/* Clear status to tell AVS co-processor we are done. */288writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);289290/* Convert firmware errors to errno's as much as possible. */291switch (val) {292case AVS_STATUS_INVALID:293ret = -EINVAL;294break;295case AVS_STATUS_NO_SUPP:296ret = -ENOTSUPP;297break;298case AVS_STATUS_NO_MAP:299ret = -ENOENT;300break;301case AVS_STATUS_MAP_SET:302ret = -EEXIST;303break;304case AVS_STATUS_FAILURE:305ret = -EIO;306break;307}308309out:310up(&priv->sem);311312return ret;313}314315static irqreturn_t irq_handler(int irq, void *data)316{317struct private_data *priv = data;318319/* AVS command completed execution. Wake up __issue_avs_command(). */320complete(&priv->done);321322return IRQ_HANDLED;323}324325static char *brcm_avs_mode_to_string(unsigned int mode)326{327switch (mode) {328case AVS_MODE_AVS:329return "AVS";330case AVS_MODE_DFS:331return "DFS";332case AVS_MODE_DVS:333return "DVS";334case AVS_MODE_DVFS:335return "DVFS";336}337return NULL;338}339340static void brcm_avs_parse_p1(u32 p1, unsigned int *mdiv_p0, unsigned int *pdiv,341unsigned int *ndiv)342{343*mdiv_p0 = (p1 >> MDIV_P0_SHIFT) & MDIV_P0_MASK;344*pdiv = (p1 >> PDIV_SHIFT) & PDIV_MASK;345*ndiv = (p1 >> NDIV_INT_SHIFT) & NDIV_INT_MASK;346}347348static void brcm_avs_parse_p2(u32 p2, unsigned int *mdiv_p1,349unsigned int *mdiv_p2, unsigned int *mdiv_p3,350unsigned int *mdiv_p4)351{352*mdiv_p4 = (p2 >> MDIV_P4_SHIFT) & MDIV_P4_MASK;353*mdiv_p3 = (p2 >> MDIV_P3_SHIFT) & MDIV_P3_MASK;354*mdiv_p2 = (p2 >> MDIV_P2_SHIFT) & MDIV_P2_MASK;355*mdiv_p1 = (p2 >> MDIV_P1_SHIFT) & MDIV_P1_MASK;356}357358static int brcm_avs_get_pmap(struct private_data *priv, struct pmap *pmap)359{360u32 args[AVS_MAX_CMD_ARGS];361int ret;362363ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, 0, 4, args);364if (ret || !pmap)365return ret;366367pmap->mode = args[0];368pmap->p1 = args[1];369pmap->p2 = args[2];370pmap->state = args[3];371372return 0;373}374375static int brcm_avs_set_pmap(struct private_data *priv, struct pmap *pmap)376{377u32 args[AVS_MAX_CMD_ARGS];378379args[0] = pmap->mode;380args[1] = pmap->p1;381args[2] = pmap->p2;382args[3] = pmap->state;383384return __issue_avs_command(priv, AVS_CMD_SET_PMAP, 4, 0, args);385}386387static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate)388{389u32 args[AVS_MAX_CMD_ARGS];390int ret;391392ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, 0, 1, args);393if (ret)394return ret;395*pstate = args[0];396397return 0;398}399400static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate)401{402u32 args[AVS_MAX_CMD_ARGS];403404args[0] = pstate;405406return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, 1, 0, args);407408}409410static u32 brcm_avs_get_voltage(void __iomem *base)411{412return readl(base + AVS_MBOX_VOLTAGE1);413}414415static u32 brcm_avs_get_frequency(void __iomem *base)416{417return readl(base + AVS_MBOX_FREQUENCY) * 1000; /* in kHz */418}419420/*421* We determine which frequencies are supported by cycling through all P-states422* and reading back what frequency we are running at for each P-state.423*/424static struct cpufreq_frequency_table *425brcm_avs_get_freq_table(struct device *dev, struct private_data *priv)426{427struct cpufreq_frequency_table *table;428unsigned int pstate;429int i, ret;430431/* Remember P-state for later */432ret = brcm_avs_get_pstate(priv, &pstate);433if (ret)434return ERR_PTR(ret);435436/*437* We allocate space for the 5 different P-STATES AVS,438* plus extra space for a terminating element.439*/440table = devm_kcalloc(dev, AVS_PSTATE_MAX + 1 + 1, sizeof(*table),441GFP_KERNEL);442if (!table)443return ERR_PTR(-ENOMEM);444445for (i = AVS_PSTATE_P0; i <= AVS_PSTATE_MAX; i++) {446ret = brcm_avs_set_pstate(priv, i);447if (ret)448return ERR_PTR(ret);449table[i].frequency = brcm_avs_get_frequency(priv->base);450table[i].driver_data = i;451}452table[i].frequency = CPUFREQ_TABLE_END;453454/* Restore P-state */455ret = brcm_avs_set_pstate(priv, pstate);456if (ret)457return ERR_PTR(ret);458459return table;460}461462/*463* To ensure the right firmware is running we need to464* - check the MAGIC matches what we expect465* - brcm_avs_get_pmap() doesn't return -ENOTSUPP or -EINVAL466* We need to set up our interrupt handling before calling brcm_avs_get_pmap()!467*/468static bool brcm_avs_is_firmware_loaded(struct private_data *priv)469{470u32 magic;471int rc;472473rc = brcm_avs_get_pmap(priv, NULL);474magic = readl(priv->base + AVS_MBOX_MAGIC);475476return (magic == AVS_FIRMWARE_MAGIC) && (rc != -ENOTSUPP) &&477(rc != -EINVAL);478}479480static unsigned int brcm_avs_cpufreq_get(unsigned int cpu)481{482struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);483struct private_data *priv;484485if (!policy)486return 0;487488priv = policy->driver_data;489490cpufreq_cpu_put(policy);491492return brcm_avs_get_frequency(priv->base);493}494495static int brcm_avs_target_index(struct cpufreq_policy *policy,496unsigned int index)497{498return brcm_avs_set_pstate(policy->driver_data,499policy->freq_table[index].driver_data);500}501502static int brcm_avs_suspend(struct cpufreq_policy *policy)503{504struct private_data *priv = policy->driver_data;505int ret;506507ret = brcm_avs_get_pmap(priv, &priv->pmap);508if (ret)509return ret;510511/*512* We can't use the P-state returned by brcm_avs_get_pmap(), since513* that's the initial P-state from when the P-map was downloaded to the514* AVS co-processor, not necessarily the P-state we are running at now.515* So, we get the current P-state explicitly.516*/517ret = brcm_avs_get_pstate(priv, &priv->pmap.state);518if (ret)519return ret;520521/* This is best effort. Nothing to do if it fails. */522(void)__issue_avs_command(priv, AVS_CMD_S2_ENTER, 0, 0, NULL);523524return 0;525}526527static int brcm_avs_resume(struct cpufreq_policy *policy)528{529struct private_data *priv = policy->driver_data;530int ret;531532/* This is best effort. Nothing to do if it fails. */533(void)__issue_avs_command(priv, AVS_CMD_S2_EXIT, 0, 0, NULL);534535ret = brcm_avs_set_pmap(priv, &priv->pmap);536if (ret == -EEXIST) {537struct platform_device *pdev = cpufreq_get_driver_data();538struct device *dev = &pdev->dev;539540dev_warn(dev, "PMAP was already set\n");541ret = 0;542}543544return ret;545}546547/*548* All initialization code that we only want to execute once goes here. Setup549* code that can be re-tried on every core (if it failed before) can go into550* brcm_avs_cpufreq_init().551*/552static int brcm_avs_prepare_init(struct platform_device *pdev)553{554struct private_data *priv;555struct device *dev;556int ret;557558dev = &pdev->dev;559priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);560if (!priv)561return -ENOMEM;562563priv->dev = dev;564sema_init(&priv->sem, 1);565init_completion(&priv->done);566platform_set_drvdata(pdev, priv);567568priv->base = __map_region(BRCM_AVS_CPU_DATA);569if (!priv->base) {570dev_err(dev, "Couldn't find property %s in device tree.\n",571BRCM_AVS_CPU_DATA);572return -ENOENT;573}574575priv->avs_intr_base = __map_region(BRCM_AVS_CPU_INTR);576if (!priv->avs_intr_base) {577dev_err(dev, "Couldn't find property %s in device tree.\n",578BRCM_AVS_CPU_INTR);579ret = -ENOENT;580goto unmap_base;581}582583priv->host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR);584585ret = devm_request_irq(dev, priv->host_irq, irq_handler,586IRQF_TRIGGER_RISING,587BRCM_AVS_HOST_INTR, priv);588if (ret && priv->host_irq >= 0) {589dev_err(dev, "IRQ request failed: %s (%d) -- %d\n",590BRCM_AVS_HOST_INTR, priv->host_irq, ret);591goto unmap_intr_base;592}593594if (brcm_avs_is_firmware_loaded(priv))595return 0;596597dev_err(dev, "AVS firmware is not loaded or doesn't support DVFS\n");598ret = -ENODEV;599600unmap_intr_base:601iounmap(priv->avs_intr_base);602unmap_base:603iounmap(priv->base);604605return ret;606}607608static void brcm_avs_prepare_uninit(struct platform_device *pdev)609{610struct private_data *priv;611612priv = platform_get_drvdata(pdev);613614iounmap(priv->avs_intr_base);615iounmap(priv->base);616}617618static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy)619{620struct cpufreq_frequency_table *freq_table;621struct platform_device *pdev;622struct private_data *priv;623struct device *dev;624int ret;625626pdev = cpufreq_get_driver_data();627priv = platform_get_drvdata(pdev);628policy->driver_data = priv;629dev = &pdev->dev;630631freq_table = brcm_avs_get_freq_table(dev, priv);632if (IS_ERR(freq_table)) {633ret = PTR_ERR(freq_table);634dev_err(dev, "Couldn't determine frequency table (%d).\n", ret);635return ret;636}637638policy->freq_table = freq_table;639640/* All cores share the same clock and thus the same policy. */641cpumask_setall(policy->cpus);642643ret = __issue_avs_command(priv, AVS_CMD_ENABLE, 0, 0, NULL);644if (!ret) {645unsigned int pstate;646647ret = brcm_avs_get_pstate(priv, &pstate);648if (!ret) {649policy->cur = freq_table[pstate].frequency;650dev_info(dev, "registered\n");651return 0;652}653}654655dev_err(dev, "couldn't initialize driver (%d)\n", ret);656657return ret;658}659660static ssize_t show_brcm_avs_pstate(struct cpufreq_policy *policy, char *buf)661{662struct private_data *priv = policy->driver_data;663unsigned int pstate;664665if (brcm_avs_get_pstate(priv, &pstate))666return sprintf(buf, "<unknown>\n");667668return sprintf(buf, "%u\n", pstate);669}670671static ssize_t show_brcm_avs_mode(struct cpufreq_policy *policy, char *buf)672{673struct private_data *priv = policy->driver_data;674struct pmap pmap;675676if (brcm_avs_get_pmap(priv, &pmap))677return sprintf(buf, "<unknown>\n");678679return sprintf(buf, "%s %u\n", brcm_avs_mode_to_string(pmap.mode),680pmap.mode);681}682683static ssize_t show_brcm_avs_pmap(struct cpufreq_policy *policy, char *buf)684{685unsigned int mdiv_p0, mdiv_p1, mdiv_p2, mdiv_p3, mdiv_p4;686struct private_data *priv = policy->driver_data;687unsigned int ndiv, pdiv;688struct pmap pmap;689690if (brcm_avs_get_pmap(priv, &pmap))691return sprintf(buf, "<unknown>\n");692693brcm_avs_parse_p1(pmap.p1, &mdiv_p0, &pdiv, &ndiv);694brcm_avs_parse_p2(pmap.p2, &mdiv_p1, &mdiv_p2, &mdiv_p3, &mdiv_p4);695696return sprintf(buf, "0x%08x 0x%08x %u %u %u %u %u %u %u %u %u\n",697pmap.p1, pmap.p2, ndiv, pdiv, mdiv_p0, mdiv_p1, mdiv_p2,698mdiv_p3, mdiv_p4, pmap.mode, pmap.state);699}700701static ssize_t show_brcm_avs_voltage(struct cpufreq_policy *policy, char *buf)702{703struct private_data *priv = policy->driver_data;704705return sprintf(buf, "0x%08x\n", brcm_avs_get_voltage(priv->base));706}707708static ssize_t show_brcm_avs_frequency(struct cpufreq_policy *policy, char *buf)709{710struct private_data *priv = policy->driver_data;711712return sprintf(buf, "0x%08x\n", brcm_avs_get_frequency(priv->base));713}714715cpufreq_freq_attr_ro(brcm_avs_pstate);716cpufreq_freq_attr_ro(brcm_avs_mode);717cpufreq_freq_attr_ro(brcm_avs_pmap);718cpufreq_freq_attr_ro(brcm_avs_voltage);719cpufreq_freq_attr_ro(brcm_avs_frequency);720721static struct freq_attr *brcm_avs_cpufreq_attr[] = {722&brcm_avs_pstate,723&brcm_avs_mode,724&brcm_avs_pmap,725&brcm_avs_voltage,726&brcm_avs_frequency,727NULL728};729730static struct cpufreq_driver brcm_avs_driver = {731.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,732.verify = cpufreq_generic_frequency_table_verify,733.target_index = brcm_avs_target_index,734.get = brcm_avs_cpufreq_get,735.suspend = brcm_avs_suspend,736.resume = brcm_avs_resume,737.init = brcm_avs_cpufreq_init,738.attr = brcm_avs_cpufreq_attr,739.name = BRCM_AVS_CPUFREQ_PREFIX,740};741742static int brcm_avs_cpufreq_probe(struct platform_device *pdev)743{744int ret;745746ret = brcm_avs_prepare_init(pdev);747if (ret)748return ret;749750brcm_avs_driver.driver_data = pdev;751752ret = cpufreq_register_driver(&brcm_avs_driver);753if (ret)754brcm_avs_prepare_uninit(pdev);755756return ret;757}758759static void brcm_avs_cpufreq_remove(struct platform_device *pdev)760{761cpufreq_unregister_driver(&brcm_avs_driver);762763brcm_avs_prepare_uninit(pdev);764}765766static const struct of_device_id brcm_avs_cpufreq_match[] = {767{ .compatible = "brcm,avs-cpu-data-mem" },768{ }769};770MODULE_DEVICE_TABLE(of, brcm_avs_cpufreq_match);771772static struct platform_driver brcm_avs_cpufreq_platdrv = {773.driver = {774.name = BRCM_AVS_CPUFREQ_NAME,775.of_match_table = brcm_avs_cpufreq_match,776},777.probe = brcm_avs_cpufreq_probe,778.remove = brcm_avs_cpufreq_remove,779};780module_platform_driver(brcm_avs_cpufreq_platdrv);781782MODULE_AUTHOR("Markus Mayer <[email protected]>");783MODULE_DESCRIPTION("CPUfreq driver for Broadcom STB AVS");784MODULE_LICENSE("GPL");785786787