Path: blob/master/drivers/cpufreq/brcmstb-avs-cpufreq.c
29266 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 __free(put_cpufreq_policy) = cpufreq_cpu_get(cpu);483struct private_data *priv;484485if (!policy)486return 0;487488priv = policy->driver_data;489490return brcm_avs_get_frequency(priv->base);491}492493static int brcm_avs_target_index(struct cpufreq_policy *policy,494unsigned int index)495{496return brcm_avs_set_pstate(policy->driver_data,497policy->freq_table[index].driver_data);498}499500static int brcm_avs_suspend(struct cpufreq_policy *policy)501{502struct private_data *priv = policy->driver_data;503int ret;504505ret = brcm_avs_get_pmap(priv, &priv->pmap);506if (ret)507return ret;508509/*510* We can't use the P-state returned by brcm_avs_get_pmap(), since511* that's the initial P-state from when the P-map was downloaded to the512* AVS co-processor, not necessarily the P-state we are running at now.513* So, we get the current P-state explicitly.514*/515ret = brcm_avs_get_pstate(priv, &priv->pmap.state);516if (ret)517return ret;518519/* This is best effort. Nothing to do if it fails. */520(void)__issue_avs_command(priv, AVS_CMD_S2_ENTER, 0, 0, NULL);521522return 0;523}524525static int brcm_avs_resume(struct cpufreq_policy *policy)526{527struct private_data *priv = policy->driver_data;528int ret;529530/* This is best effort. Nothing to do if it fails. */531(void)__issue_avs_command(priv, AVS_CMD_S2_EXIT, 0, 0, NULL);532533ret = brcm_avs_set_pmap(priv, &priv->pmap);534if (ret == -EEXIST) {535struct platform_device *pdev = cpufreq_get_driver_data();536struct device *dev = &pdev->dev;537538dev_warn(dev, "PMAP was already set\n");539ret = 0;540}541542return ret;543}544545/*546* All initialization code that we only want to execute once goes here. Setup547* code that can be re-tried on every core (if it failed before) can go into548* brcm_avs_cpufreq_init().549*/550static int brcm_avs_prepare_init(struct platform_device *pdev)551{552struct private_data *priv;553struct device *dev;554int ret;555556dev = &pdev->dev;557priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);558if (!priv)559return -ENOMEM;560561priv->dev = dev;562sema_init(&priv->sem, 1);563init_completion(&priv->done);564platform_set_drvdata(pdev, priv);565566priv->base = __map_region(BRCM_AVS_CPU_DATA);567if (!priv->base) {568dev_err(dev, "Couldn't find property %s in device tree.\n",569BRCM_AVS_CPU_DATA);570return -ENOENT;571}572573priv->avs_intr_base = __map_region(BRCM_AVS_CPU_INTR);574if (!priv->avs_intr_base) {575dev_err(dev, "Couldn't find property %s in device tree.\n",576BRCM_AVS_CPU_INTR);577ret = -ENOENT;578goto unmap_base;579}580581priv->host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR);582583ret = devm_request_irq(dev, priv->host_irq, irq_handler,584IRQF_TRIGGER_RISING,585BRCM_AVS_HOST_INTR, priv);586if (ret && priv->host_irq >= 0) {587dev_err(dev, "IRQ request failed: %s (%d) -- %d\n",588BRCM_AVS_HOST_INTR, priv->host_irq, ret);589goto unmap_intr_base;590}591592if (brcm_avs_is_firmware_loaded(priv))593return 0;594595dev_err(dev, "AVS firmware is not loaded or doesn't support DVFS\n");596ret = -ENODEV;597598unmap_intr_base:599iounmap(priv->avs_intr_base);600unmap_base:601iounmap(priv->base);602603return ret;604}605606static void brcm_avs_prepare_uninit(struct platform_device *pdev)607{608struct private_data *priv;609610priv = platform_get_drvdata(pdev);611612iounmap(priv->avs_intr_base);613iounmap(priv->base);614}615616static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy)617{618struct cpufreq_frequency_table *freq_table;619struct platform_device *pdev;620struct private_data *priv;621struct device *dev;622int ret;623624pdev = cpufreq_get_driver_data();625priv = platform_get_drvdata(pdev);626policy->driver_data = priv;627dev = &pdev->dev;628629freq_table = brcm_avs_get_freq_table(dev, priv);630if (IS_ERR(freq_table)) {631ret = PTR_ERR(freq_table);632dev_err(dev, "Couldn't determine frequency table (%d).\n", ret);633return ret;634}635636policy->freq_table = freq_table;637638/* All cores share the same clock and thus the same policy. */639cpumask_setall(policy->cpus);640641ret = __issue_avs_command(priv, AVS_CMD_ENABLE, 0, 0, NULL);642if (!ret) {643unsigned int pstate;644645ret = brcm_avs_get_pstate(priv, &pstate);646if (!ret) {647policy->cur = freq_table[pstate].frequency;648dev_info(dev, "registered\n");649return 0;650}651}652653dev_err(dev, "couldn't initialize driver (%d)\n", ret);654655return ret;656}657658static ssize_t show_brcm_avs_pstate(struct cpufreq_policy *policy, char *buf)659{660struct private_data *priv = policy->driver_data;661unsigned int pstate;662663if (brcm_avs_get_pstate(priv, &pstate))664return sprintf(buf, "<unknown>\n");665666return sprintf(buf, "%u\n", pstate);667}668669static ssize_t show_brcm_avs_mode(struct cpufreq_policy *policy, char *buf)670{671struct private_data *priv = policy->driver_data;672struct pmap pmap;673674if (brcm_avs_get_pmap(priv, &pmap))675return sprintf(buf, "<unknown>\n");676677return sprintf(buf, "%s %u\n", brcm_avs_mode_to_string(pmap.mode),678pmap.mode);679}680681static ssize_t show_brcm_avs_pmap(struct cpufreq_policy *policy, char *buf)682{683unsigned int mdiv_p0, mdiv_p1, mdiv_p2, mdiv_p3, mdiv_p4;684struct private_data *priv = policy->driver_data;685unsigned int ndiv, pdiv;686struct pmap pmap;687688if (brcm_avs_get_pmap(priv, &pmap))689return sprintf(buf, "<unknown>\n");690691brcm_avs_parse_p1(pmap.p1, &mdiv_p0, &pdiv, &ndiv);692brcm_avs_parse_p2(pmap.p2, &mdiv_p1, &mdiv_p2, &mdiv_p3, &mdiv_p4);693694return sprintf(buf, "0x%08x 0x%08x %u %u %u %u %u %u %u %u %u\n",695pmap.p1, pmap.p2, ndiv, pdiv, mdiv_p0, mdiv_p1, mdiv_p2,696mdiv_p3, mdiv_p4, pmap.mode, pmap.state);697}698699static ssize_t show_brcm_avs_voltage(struct cpufreq_policy *policy, char *buf)700{701struct private_data *priv = policy->driver_data;702703return sprintf(buf, "0x%08x\n", brcm_avs_get_voltage(priv->base));704}705706static ssize_t show_brcm_avs_frequency(struct cpufreq_policy *policy, char *buf)707{708struct private_data *priv = policy->driver_data;709710return sprintf(buf, "0x%08x\n", brcm_avs_get_frequency(priv->base));711}712713cpufreq_freq_attr_ro(brcm_avs_pstate);714cpufreq_freq_attr_ro(brcm_avs_mode);715cpufreq_freq_attr_ro(brcm_avs_pmap);716cpufreq_freq_attr_ro(brcm_avs_voltage);717cpufreq_freq_attr_ro(brcm_avs_frequency);718719static struct freq_attr *brcm_avs_cpufreq_attr[] = {720&brcm_avs_pstate,721&brcm_avs_mode,722&brcm_avs_pmap,723&brcm_avs_voltage,724&brcm_avs_frequency,725NULL726};727728static struct cpufreq_driver brcm_avs_driver = {729.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,730.verify = cpufreq_generic_frequency_table_verify,731.target_index = brcm_avs_target_index,732.get = brcm_avs_cpufreq_get,733.suspend = brcm_avs_suspend,734.resume = brcm_avs_resume,735.init = brcm_avs_cpufreq_init,736.attr = brcm_avs_cpufreq_attr,737.name = BRCM_AVS_CPUFREQ_PREFIX,738};739740static int brcm_avs_cpufreq_probe(struct platform_device *pdev)741{742int ret;743744ret = brcm_avs_prepare_init(pdev);745if (ret)746return ret;747748brcm_avs_driver.driver_data = pdev;749750ret = cpufreq_register_driver(&brcm_avs_driver);751if (ret)752brcm_avs_prepare_uninit(pdev);753754return ret;755}756757static void brcm_avs_cpufreq_remove(struct platform_device *pdev)758{759cpufreq_unregister_driver(&brcm_avs_driver);760761brcm_avs_prepare_uninit(pdev);762}763764static const struct of_device_id brcm_avs_cpufreq_match[] = {765{ .compatible = "brcm,avs-cpu-data-mem" },766{ }767};768MODULE_DEVICE_TABLE(of, brcm_avs_cpufreq_match);769770static struct platform_driver brcm_avs_cpufreq_platdrv = {771.driver = {772.name = BRCM_AVS_CPUFREQ_NAME,773.of_match_table = brcm_avs_cpufreq_match,774},775.probe = brcm_avs_cpufreq_probe,776.remove = brcm_avs_cpufreq_remove,777};778module_platform_driver(brcm_avs_cpufreq_platdrv);779780MODULE_AUTHOR("Markus Mayer <[email protected]>");781MODULE_DESCRIPTION("CPUfreq driver for Broadcom STB AVS");782MODULE_LICENSE("GPL");783784785