Char/Misc driver changes for 5.6-rc1

Here is the big char/misc/whatever driver changes for 5.6-rc1
 
 Included in here are loads of things from a variety of different driver
 subsystems:
 	- soundwire updates
 	- binder updates
 	- nvmem updates
 	- firmware drivers updates
 	- extcon driver updates
 	- various misc driver updates
 	- fpga driver updates
 	- interconnect subsystem and driver updates
 	- bus driver updates
 	- uio driver updates
 	- mei driver updates
 	- w1 driver cleanups
 	- various other small driver updates
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXjFKeQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynjVACgg6JWfOyPCnz3GfRD1vQZyUl+Hg0An1H+Eh08
 +LQk5Qpb3vVwBpCp6qR3
 =MB+D
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here is the big char/misc/whatever driver changes for 5.6-rc1

  Included in here are loads of things from a variety of different
  driver subsystems:
   - soundwire updates
   - binder updates
   - nvmem updates
   - firmware drivers updates
   - extcon driver updates
   - various misc driver updates
   - fpga driver updates
   - interconnect subsystem and driver updates
   - bus driver updates
   - uio driver updates
   - mei driver updates
   - w1 driver cleanups
   - various other small driver updates

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'char-misc-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (86 commits)
  mei: me: add jasper point DID
  char: hpet: Use flexible-array member
  binder: fix log spam for existing debugfs file creation.
  mei: me: add comet point (lake) H device ids
  nvmem: add QTI SDAM driver
  dt-bindings: nvmem: add binding for QTI SPMI SDAM
  dt-bindings: imx-ocotp: Add i.MX8MP compatible
  dt-bindings: soundwire: fix example
  soundwire: cadence: fix kernel-doc parameter descriptions
  soundwire: intel: report slave_ids for each link to SOF driver
  siox: Use the correct style for SPDX License Identifier
  w1: omap-hdq: Simplify driver with PM runtime autosuspend
  firmware: stratix10-svc: Remove unneeded semicolon
  firmware: google: Probe for a GSMI handler in firmware
  firmware: google: Unregister driver_info on failure and exit in gsmi
  firmware: google: Release devices before unregistering the bus
  slimbus: qcom: add missed clk_disable_unprepare in remove
  slimbus: Use the correct style for SPDX License Identifier
  slimbus: qcom-ngd-ctrl: Use dma_request_chan() instead dma_request_slave_channel()
  dt-bindings: SLIMBus: add slim devices optional properties
  ...
This commit is contained in:
Linus Torvalds 2020-01-29 10:35:54 -08:00
commit 701a9c8092
76 changed files with 3133 additions and 702 deletions

View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/qcom,msm8916.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm MSM8916 Network-On-Chip interconnect
maintainers:
- Georgi Djakov <georgi.djakov@linaro.org>
description: |
The Qualcomm MSM8916 interconnect providers support adjusting the
bandwidth requirements between the various NoC fabrics.
properties:
compatible:
enum:
- qcom,msm8916-bimc
- qcom,msm8916-pcnoc
- qcom,msm8916-snoc
reg:
maxItems: 1
'#interconnect-cells':
const: 1
clock-names:
items:
- const: bus
- const: bus_a
clocks:
items:
- description: Bus Clock
- description: Bus A Clock
required:
- compatible
- reg
- '#interconnect-cells'
- clock-names
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmcc.h>
bimc: interconnect@400000 {
compatible = "qcom,msm8916-bimc";
reg = <0x00400000 0x62000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
<&rpmcc RPM_SMD_BIMC_A_CLK>;
};
pcnoc: interconnect@500000 {
compatible = "qcom,msm8916-pcnoc";
reg = <0x00500000 0x11000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_PCNOC_CLK>,
<&rpmcc RPM_SMD_PCNOC_A_CLK>;
};
snoc: interconnect@580000 {
compatible = "qcom,msm8916-snoc";
reg = <0x00580000 0x14000>;
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
<&rpmcc RPM_SMD_SNOC_A_CLK>;
};

View File

@ -2,7 +2,7 @@ Freescale i.MX6 On-Chip OTP Controller (OCOTP) device tree bindings
This binding represents the on-chip eFuse OTP controller found on
i.MX6Q/D, i.MX6DL/S, i.MX6SL, i.MX6SX, i.MX6UL, i.MX6ULL/ULZ, i.MX6SLL,
i.MX7D/S, i.MX7ULP, i.MX8MQ, i.MX8MM and i.MX8MN SoCs.
i.MX7D/S, i.MX7ULP, i.MX8MQ, i.MX8MM, i.MX8MN and i.MX8MP SoCs.
Required properties:
- compatible: should be one of
@ -17,6 +17,7 @@ Required properties:
"fsl,imx8mq-ocotp" (i.MX8MQ),
"fsl,imx8mm-ocotp" (i.MX8MM),
"fsl,imx8mn-ocotp" (i.MX8MN),
"fsl,imx8mp-ocotp" (i.MX8MP),
followed by "syscon".
- #address-cells : Should be 1
- #size-cells : Should be 1

View File

@ -0,0 +1,84 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/qcom,spmi-sdam.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. SPMI SDAM DT bindings
maintainers:
- Shyam Kumar Thella <sthella@codeaurora.org>
description: |
The SDAM provides scratch register space for the PMIC clients. This
memory can be used by software to store information or communicate
to/from the PBUS.
allOf:
- $ref: "nvmem.yaml#"
properties:
compatible:
enum:
- qcom,spmi-sdam
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 1
ranges: true
required:
- compatible
- reg
- ranges
patternProperties:
"^.*@[0-9a-f]+$":
type: object
properties:
reg:
maxItems: 1
description:
Offset and size in bytes within the storage device.
bits:
$ref: /schemas/types.yaml#/definitions/uint32-array
maxItems: 1
items:
items:
- minimum: 0
maximum: 7
description:
Offset in bit within the address range specified by reg.
- minimum: 1
description:
Size in bit within the address range specified by reg.
required:
- reg
additionalProperties: false
examples:
- |
sdam_1: nvram@b000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "qcom,spmi-sdam";
reg = <0xb000 0x100>;
ranges = <0 0xb000 0x100>;
/* Data cells */
restart_reason: restart@50 {
reg = <0x50 0x1>;
bits = <6 2>;
};
};
...

View File

@ -32,6 +32,10 @@ Required property for SLIMbus child node if it is present:
Product Code, shall be in lower case hexadecimal with leading
zeroes suppressed
Optional property for SLIMbus child node if it is present:
- slim-ifc-dev - Should be phandle to SLIMBus Interface device.
Required for devices which deal with streams.
SLIMbus example for Qualcomm's slimbus manager component:
slim@28080000 {
@ -43,8 +47,14 @@ SLIMbus example for Qualcomm's slimbus manager component:
#address-cells = <2>;
#size-cell = <0>;
codec_ifd: ifd@0,0{
compatible = "slim217,60";
reg = <0 0>;
};
codec: wcd9310@1,0{
compatible = "slim217,60";
reg = <1 0>;
slim-ifc-dev = <&codec_ifd>;
};
};

View File

@ -0,0 +1,167 @@
Qualcomm SoundWire Controller Bindings
This binding describes the Qualcomm SoundWire Controller along with its
board specific bus parameters.
- compatible:
Usage: required
Value type: <stringlist>
Definition: must be "qcom,soundwire-v<MAJOR>.<MINOR>.<STEP>",
Example:
"qcom,soundwire-v1.3.0"
"qcom,soundwire-v1.5.0"
"qcom,soundwire-v1.6.0"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: the base address and size of SoundWire controller
address space.
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify the SoundWire Controller IRQ
- clock-names:
Usage: required
Value type: <stringlist>
Definition: should be "iface" for SoundWire Controller interface clock
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify the SoundWire Controller interface clock
- #sound-dai-cells:
Usage: required
Value type: <u32>
Definition: must be 1 for digital audio interfaces on the controller.
- qcom,dout-ports:
Usage: required
Value type: <u32>
Definition: must be count of data out ports
- qcom,din-ports:
Usage: required
Value type: <u32>
Definition: must be count of data in ports
- qcom,ports-offset1:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify payload transport window offset1 of each
data port. Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-offset2:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify payload transport window offset2 of each
data port. Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-sinterval-low:
Usage: required
Value type: <prop-encoded-array>
Definition: should be sample interval low of each data port.
Out ports followed by In ports. Used for Sample Interval
calculation.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-word-length:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be size of payload channel sample.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-block-pack-mode:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be 0 or 1 to indicate the block packing mode.
0 to indicate Blocks are per Channel
1 to indicate Blocks are per Port.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-block-group-count:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be in range 1 to 4 to indicate how many sample
intervals are combined into a payload.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-lane-control:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be in range 0 to 7 to identify which data lane
the data port uses.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-hstart:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be number identifying lowerst numbered coloum in
SoundWire Frame, i.e. left edge of the Transport sub-frame
for each port. Values between 0 and 15 are valid.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-hstop:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be number identifying highest numbered coloum in
SoundWire Frame, i.e. the right edge of the Transport
sub-frame for each port. Values between 0 and 15 are valid.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,dports-type:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be one of the following types
0 for reduced port
1 for simple ports
2 for full port
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
Note:
More Information on detail of encoding of these fields can be
found in MIPI Alliance SoundWire 1.0 Specifications.
= SoundWire devices
Each subnode of the bus represents SoundWire device attached to it.
The properties of these nodes are defined by the individual bindings.
= EXAMPLE
The following example represents a SoundWire controller on DB845c board
which has controller integrated inside WCD934x codec on SDM845 SoC.
soundwire: soundwire@c85 {
compatible = "qcom,soundwire-v1.3.0";
reg = <0xc85 0x20>;
interrupts = <20 IRQ_TYPE_EDGE_RISING>;
clocks = <&wcc>;
clock-names = "iface";
#sound-dai-cells = <1>;
qcom,dports-type = <0>;
qcom,dout-ports = <6>;
qcom,din-ports = <2>;
qcom,ports-sinterval-low = /bits/ 8 <0x07 0x1F 0x3F 0x7 0x1F 0x3F 0x0F 0x0F>;
qcom,ports-offset1 = /bits/ 8 <0x01 0x02 0x0C 0x6 0x12 0x0D 0x07 0x0A >;
qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x1F 0x00 0x00 0x1F 0x00 0x00>;
/* Left Speaker */
left{
....
};
/* Right Speaker */
right{
....
};
};

View File

@ -69,6 +69,7 @@ examples:
reg = <0 1>;
powerdown-gpios = <&wcdpinctrl 2 0>;
#thermal-sensor-cells = <0>;
#sound-dai-cells = <0>;
};
speaker@0,2 {
@ -76,6 +77,7 @@ examples:
reg = <0 2>;
powerdown-gpios = <&wcdpinctrl 2 0>;
#thermal-sensor-cells = <0>;
#sound-dai-cells = <0>;
};
};

View File

@ -91,3 +91,25 @@ Interconnect consumers are the clients which use the interconnect APIs to
get paths between endpoints and set their bandwidth/latency/QoS requirements
for these interconnect paths. These interfaces are not currently
documented.
Interconnect debugfs interfaces
-------------------------------
Like several other subsystems interconnect will create some files for debugging
and introspection. Files in debugfs are not considered ABI so application
software shouldn't rely on format details change between kernel versions.
``/sys/kernel/debug/interconnect/interconnect_summary``:
Show all interconnect nodes in the system with their aggregated bandwidth
request. Indented under each node show bandwidth requests from each device.
``/sys/kernel/debug/interconnect/interconnect_graph``:
Show the interconnect graph in the graphviz dot format. It shows all
interconnect nodes and links in the system and groups together nodes from the
same provider as subgraphs. The format is human-readable and can also be piped
through dot to generate diagrams in many graphical formats::
$ cat /sys/kernel/debug/interconnect/interconnect_graph | \
dot -Tsvg > interconnect_graph.svg

View File

@ -5199,10 +5199,11 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
struct binder_proc *proc, *itr;
struct binder_device *binder_dev;
struct binderfs_info *info;
struct dentry *binder_binderfs_dir_entry_proc = NULL;
bool existing_pid = false;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
current->group_leader->pid, current->pid);
@ -5235,19 +5236,24 @@ static int binder_open(struct inode *nodp, struct file *filp)
filp->private_data = proc;
mutex_lock(&binder_procs_lock);
hlist_for_each_entry(itr, &binder_procs, proc_node) {
if (itr->pid == proc->pid) {
existing_pid = true;
break;
}
}
hlist_add_head(&proc->proc_node, &binder_procs);
mutex_unlock(&binder_procs_lock);
if (binder_debugfs_dir_entry_proc) {
if (binder_debugfs_dir_entry_proc && !existing_pid) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
/*
* proc debug entries are shared between contexts, so
* this will fail if the process tries to open the driver
* again with a different context. The priting code will
* anyway print all contexts that a given PID has, so this
* is not a problem.
* proc debug entries are shared between contexts.
* Only create for the first PID to avoid debugfs log spamming
* The printing code will anyway print all contexts for a given
* PID so this is not a problem.
*/
proc->debugfs_entry = debugfs_create_file(strbuf, 0444,
binder_debugfs_dir_entry_proc,
@ -5255,19 +5261,16 @@ static int binder_open(struct inode *nodp, struct file *filp)
&proc_fops);
}
if (binder_binderfs_dir_entry_proc) {
if (binder_binderfs_dir_entry_proc && !existing_pid) {
char strbuf[11];
struct dentry *binderfs_entry;
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
/*
* Similar to debugfs, the process specific log file is shared
* between contexts. If the file has already been created for a
* process, the following binderfs_create_file() call will
* fail with error code EEXIST if another context of the same
* process invoked binder_open(). This is ok since same as
* debugfs, the log file will contain information on all
* contexts of a given PID.
* between contexts. Only create for the first PID.
* This is ok since same as debugfs, the log file will contain
* information on all contexts of a given PID.
*/
binderfs_entry = binderfs_create_file(binder_binderfs_dir_entry_proc,
strbuf, &proc_fops, (void *)(unsigned long)proc->pid);
@ -5277,10 +5280,8 @@ static int binder_open(struct inode *nodp, struct file *filp)
int error;
error = PTR_ERR(binderfs_entry);
if (error != -EEXIST) {
pr_warn("Unable to create file %s in binderfs (error %d)\n",
strbuf, error);
}
pr_warn("Unable to create file %s in binderfs (error %d)\n",
strbuf, error);
}
}

View File

@ -715,9 +715,9 @@ EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev)
{
struct fsl_mc_device *mc_bus_dev, *endpoint;
struct fsl_mc_obj_desc endpoint_desc = { 0 };
struct dprc_endpoint endpoint1 = { 0 };
struct dprc_endpoint endpoint2 = { 0 };
struct fsl_mc_obj_desc endpoint_desc = {{ 0 }};
struct dprc_endpoint endpoint1 = {{ 0 }};
struct dprc_endpoint endpoint2 = {{ 0 }};
int state, err;
mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);

View File

@ -110,7 +110,7 @@ struct hpets {
unsigned long hp_delta;
unsigned int hp_ntimer;
unsigned int hp_which;
struct hpet_dev hp_dev[1];
struct hpet_dev hp_dev[];
};
static struct hpets *hpets;

View File

@ -77,8 +77,6 @@ struct arizona_extcon_info {
const struct arizona_micd_range *micd_ranges;
int num_micd_ranges;
int micd_timeout;
bool micd_reva;
bool micd_clamp;
@ -310,9 +308,13 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
}
if (info->micd_reva) {
regmap_write(arizona->regmap, 0x80, 0x3);
regmap_write(arizona->regmap, 0x294, 0);
regmap_write(arizona->regmap, 0x80, 0x0);
const struct reg_sequence reva[] = {
{ 0x80, 0x3 },
{ 0x294, 0x0 },
{ 0x80, 0x0 },
};
regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
}
if (info->detecting && arizona->pdata.micd_software_compare)
@ -361,9 +363,13 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
snd_soc_dapm_sync(dapm);
if (info->micd_reva) {
regmap_write(arizona->regmap, 0x80, 0x3);
regmap_write(arizona->regmap, 0x294, 2);
regmap_write(arizona->regmap, 0x80, 0x0);
const struct reg_sequence reva[] = {
{ 0x80, 0x3 },
{ 0x294, 0x2 },
{ 0x80, 0x0 },
};
regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
}
ret = regulator_allow_bypass(info->micvdd, true);
@ -527,67 +533,65 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
struct arizona *arizona = info->arizona;
int id_gpio = arizona->pdata.hpdet_id_gpio;
if (!arizona->pdata.hpdet_acc_id)
return 0;
/*
* If we're using HPDET for accessory identification we need
* to take multiple measurements, step through them in sequence.
*/
if (arizona->pdata.hpdet_acc_id) {
info->hpdet_res[info->num_hpdet_res++] = *reading;
info->hpdet_res[info->num_hpdet_res++] = *reading;
/* Only check the mic directly if we didn't already ID it */
if (id_gpio && info->num_hpdet_res == 1) {
dev_dbg(arizona->dev, "Measuring mic\n");
/* Only check the mic directly if we didn't already ID it */
if (id_gpio && info->num_hpdet_res == 1) {
dev_dbg(arizona->dev, "Measuring mic\n");
regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
ARIZONA_ACCDET_MODE_MASK |
ARIZONA_ACCDET_SRC,
ARIZONA_ACCDET_MODE_HPR |
info->micd_modes[0].src);
gpio_set_value_cansleep(id_gpio, 1);
regmap_update_bits(arizona->regmap,
ARIZONA_HEADPHONE_DETECT_1,
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
return -EAGAIN;
}
/* OK, got both. Now, compare... */
dev_dbg(arizona->dev, "HPDET measured %d %d\n",
info->hpdet_res[0], info->hpdet_res[1]);
/* Take the headphone impedance for the main report */
*reading = info->hpdet_res[0];
/* Sometimes we get false readings due to slow insert */
if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
dev_dbg(arizona->dev, "Retrying high impedance\n");
info->num_hpdet_res = 0;
info->hpdet_retried = true;
arizona_start_hpdet_acc_id(info);
pm_runtime_put(info->dev);
return -EAGAIN;
}
/*
* If we measure the mic as high impedance
*/
if (!id_gpio || info->hpdet_res[1] > 50) {
dev_dbg(arizona->dev, "Detected mic\n");
*mic = true;
info->detecting = true;
} else {
dev_dbg(arizona->dev, "Detected headphone\n");
}
/* Make sure everything is reset back to the real polarity */
regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
ARIZONA_ACCDET_MODE_MASK |
ARIZONA_ACCDET_SRC,
ARIZONA_ACCDET_MODE_HPR |
info->micd_modes[0].src);
gpio_set_value_cansleep(id_gpio, 1);
regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
return -EAGAIN;
}
/* OK, got both. Now, compare... */
dev_dbg(arizona->dev, "HPDET measured %d %d\n",
info->hpdet_res[0], info->hpdet_res[1]);
/* Take the headphone impedance for the main report */
*reading = info->hpdet_res[0];
/* Sometimes we get false readings due to slow insert */
if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
dev_dbg(arizona->dev, "Retrying high impedance\n");
info->num_hpdet_res = 0;
info->hpdet_retried = true;
arizona_start_hpdet_acc_id(info);
pm_runtime_put(info->dev);
return -EAGAIN;
}
/*
* If we measure the mic as high impedance
*/
if (!id_gpio || info->hpdet_res[1] > 50) {
dev_dbg(arizona->dev, "Detected mic\n");
*mic = true;
info->detecting = true;
} else {
dev_dbg(arizona->dev, "Detected headphone\n");
}
/* Make sure everything is reset back to the real polarity */
regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
ARIZONA_ACCDET_SRC, info->micd_modes[0].src);
return 0;
}
@ -662,11 +666,6 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
if (id_gpio)
gpio_set_value_cansleep(id_gpio, 0);
/* Revert back to MICDET mode */
regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* If we have a mic then reenable MICDET */
if (mic || info->mic)
arizona_start_mic(info);
@ -699,8 +698,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
info->hpdet_active = true;
if (info->mic)
arizona_stop_mic(info);
arizona_stop_mic(info);
arizona_extcon_hp_clamp(info, true);
@ -724,8 +722,8 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
return;
err:
regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
arizona_extcon_hp_clamp(info, false);
pm_runtime_put_autosuspend(info->dev);
/* Just report headphone */
ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
@ -781,9 +779,6 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
return;
err:
regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
if (ret != 0)
@ -806,75 +801,58 @@ static void arizona_micd_timeout_work(struct work_struct *work)
arizona_identify_headphone(info);
arizona_stop_mic(info);
mutex_unlock(&info->lock);
}
static void arizona_micd_detect(struct work_struct *work)
static int arizona_micd_adc_read(struct arizona_extcon_info *info)
{
struct arizona_extcon_info *info = container_of(work,
struct arizona_extcon_info,
micd_detect_work.work);
struct arizona *arizona = info->arizona;
unsigned int val = 0, lvl;
int ret, i, key;
unsigned int val;
int ret;
cancel_delayed_work_sync(&info->micd_timeout_work);
/* Must disable MICD before we read the ADCVAL */
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA, 0);
mutex_lock(&info->lock);
/* If the cable was removed while measuring ignore the result */
ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
mutex_unlock(&info->lock);
return;
} else if (!ret) {
dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
mutex_unlock(&info->lock);
return;
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to read MICDET_ADCVAL: %d\n", ret);
return ret;
}
if (info->detecting && arizona->pdata.micd_software_compare) {
/* Must disable MICD before we read the ADCVAL */
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA, 0);
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to read MICDET_ADCVAL: %d\n",
ret);
mutex_unlock(&info->lock);
return;
}
dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val);
dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val);
val &= ARIZONA_MICDET_ADCVAL_MASK;
if (val < ARRAY_SIZE(arizona_micd_levels))
val = arizona_micd_levels[val];
else
val = INT_MAX;
val &= ARIZONA_MICDET_ADCVAL_MASK;
if (val < ARRAY_SIZE(arizona_micd_levels))
val = arizona_micd_levels[val];
else
val = INT_MAX;
if (val <= QUICK_HEADPHONE_MAX_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0;
else if (val <= MICROPHONE_MIN_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1;
else if (val <= MICROPHONE_MAX_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8;
else
val = ARIZONA_MICD_LVL_8;
if (val <= QUICK_HEADPHONE_MAX_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0;
else if (val <= MICROPHONE_MIN_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1;
else if (val <= MICROPHONE_MAX_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8;
else
val = ARIZONA_MICD_LVL_8;
}
return val;
}
static int arizona_micd_read(struct arizona_extcon_info *info)
{
struct arizona *arizona = info->arizona;
unsigned int val = 0;
int ret, i;
for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to read MICDET: %d\n", ret);
mutex_unlock(&info->lock);
return;
return ret;
}
dev_dbg(arizona->dev, "MICDET: %x\n", val);
@ -882,29 +860,44 @@ static void arizona_micd_detect(struct work_struct *work)
if (!(val & ARIZONA_MICD_VALID)) {
dev_warn(arizona->dev,
"Microphone detection state invalid\n");
mutex_unlock(&info->lock);
return;
return -EINVAL;
}
}
if (i == 10 && !(val & MICD_LVL_0_TO_8)) {
dev_err(arizona->dev, "Failed to get valid MICDET value\n");
mutex_unlock(&info->lock);
return;
return -EINVAL;
}
return val;
}
static int arizona_micdet_reading(void *priv)
{
struct arizona_extcon_info *info = priv;
struct arizona *arizona = info->arizona;
int ret, val;
if (info->detecting && arizona->pdata.micd_software_compare)
ret = arizona_micd_adc_read(info);
else
ret = arizona_micd_read(info);
if (ret < 0)
return ret;
val = ret;
/* Due to jack detect this should never happen */
if (!(val & ARIZONA_MICD_STS)) {
dev_warn(arizona->dev, "Detected open circuit\n");
info->mic = false;
arizona_stop_mic(info);
info->detecting = false;
arizona_identify_headphone(info);
goto handled;
return 0;
}
/* If we got a high impedence we should have a headset, report it. */
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
if (val & ARIZONA_MICD_LVL_8) {
info->mic = true;
info->detecting = false;
@ -923,7 +916,7 @@ static void arizona_micd_detect(struct work_struct *work)
ret);
}
goto handled;
return 0;
}
/* If we detected a lower impedence during initial startup
@ -932,15 +925,13 @@ static void arizona_micd_detect(struct work_struct *work)
* plain headphones. If both polarities report a low
* impedence then give up and report headphones.
*/
if (info->detecting && (val & MICD_LVL_1_TO_7)) {
if (val & MICD_LVL_1_TO_7) {
if (info->jack_flips >= info->micd_num_modes * 10) {
dev_dbg(arizona->dev, "Detected HP/line\n");
info->detecting = false;
arizona_identify_headphone(info);
arizona_stop_mic(info);
} else {
info->micd_mode++;
if (info->micd_mode == info->micd_num_modes)
@ -948,11 +939,43 @@ static void arizona_micd_detect(struct work_struct *work)
arizona_extcon_set_mode(info, info->micd_mode);
info->jack_flips++;
if (arizona->pdata.micd_software_compare)
regmap_update_bits(arizona->regmap,
ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA,
ARIZONA_MICD_ENA);
queue_delayed_work(system_power_efficient_wq,
&info->micd_timeout_work,
msecs_to_jiffies(arizona->pdata.micd_timeout));
}
goto handled;
return 0;
}
/*
* If we're still detecting and we detect a short then we've
* got a headphone.
*/
dev_dbg(arizona->dev, "Headphone detected\n");
info->detecting = false;
arizona_identify_headphone(info);
return 0;
}
static int arizona_button_reading(void *priv)
{
struct arizona_extcon_info *info = priv;
struct arizona *arizona = info->arizona;
int val, key, lvl, i;
val = arizona_micd_read(info);
if (val < 0)
return val;
/*
* If we're still detecting and we detect a short then we've
* got a headphone. Otherwise it's a button press.
@ -968,20 +991,13 @@ static void arizona_micd_detect(struct work_struct *work)
input_report_key(info->input,
info->micd_ranges[i].key, 0);
WARN_ON(!lvl);
WARN_ON(ffs(lvl) - 1 >= info->num_micd_ranges);
if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
key = info->micd_ranges[ffs(lvl) - 1].key;
input_report_key(info->input, key, 1);
input_sync(info->input);
} else {
dev_err(arizona->dev, "Button out of range\n");
}
} else if (info->detecting) {
dev_dbg(arizona->dev, "Headphone detected\n");
info->detecting = false;
arizona_stop_mic(info);
arizona_identify_headphone(info);
} else {
dev_warn(arizona->dev, "Button with no mic: %x\n",
val);
@ -995,19 +1011,39 @@ static void arizona_micd_detect(struct work_struct *work)
arizona_extcon_pulse_micbias(info);
}
handled:
if (info->detecting) {
if (arizona->pdata.micd_software_compare)
regmap_update_bits(arizona->regmap,
ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA,
ARIZONA_MICD_ENA);
return 0;
}
queue_delayed_work(system_power_efficient_wq,
&info->micd_timeout_work,
msecs_to_jiffies(info->micd_timeout));
static void arizona_micd_detect(struct work_struct *work)
{
struct arizona_extcon_info *info = container_of(work,
struct arizona_extcon_info,
micd_detect_work.work);
struct arizona *arizona = info->arizona;
int ret;
cancel_delayed_work_sync(&info->micd_timeout_work);
mutex_lock(&info->lock);
/* If the cable was removed while measuring ignore the result */
ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
mutex_unlock(&info->lock);
return;
} else if (!ret) {
dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
mutex_unlock(&info->lock);
return;
}
if (info->detecting)
arizona_micdet_reading(info);
else
arizona_button_reading(info);
pm_runtime_mark_last_busy(info->dev);
mutex_unlock(&info->lock);
}
@ -1125,7 +1161,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
msecs_to_jiffies(HPDET_DEBOUNCE));
if (cancelled_mic) {
int micd_timeout = info->micd_timeout;
int micd_timeout = arizona->pdata.micd_timeout;
queue_delayed_work(system_power_efficient_wq,
&info->micd_timeout_work,
@ -1145,11 +1181,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
dev_err(arizona->dev, "Mechanical report failed: %d\n",
ret);
if (!arizona->pdata.hpdet_acc_id) {
info->detecting = true;
info->mic = false;
info->jack_flips = 0;
info->detecting = true;
info->mic = false;
info->jack_flips = 0;
if (!arizona->pdata.hpdet_acc_id) {
arizona_start_mic(info);
} else {
queue_delayed_work(system_power_efficient_wq,
@ -1202,11 +1238,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
}
if (arizona->pdata.micd_timeout)
info->micd_timeout = arizona->pdata.micd_timeout;
else
info->micd_timeout = DEFAULT_MICD_TIMEOUT;
out:
/* Clear trig_sts to make sure DCVDD is not forced up */
regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
@ -1435,6 +1466,9 @@ static int arizona_extcon_probe(struct platform_device *pdev)
info->input->name = "Headset";
info->input->phys = "arizona/extcon";
if (!pdata->micd_timeout)
pdata->micd_timeout = DEFAULT_MICD_TIMEOUT;
if (pdata->num_micd_configs) {
info->micd_modes = pdata->micd_configs;
info->micd_num_modes = pdata->num_micd_configs;

View File

@ -249,7 +249,7 @@ static int sm5502_muic_set_path(struct sm5502_muic_info *info,
dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",
con_sw);
return -EINVAL;
};
}
switch (vbus_sw) {
case VBUSIN_SWITCH_OPEN:
@ -268,7 +268,7 @@ static int sm5502_muic_set_path(struct sm5502_muic_info *info,
default:
dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw);
return -EINVAL;
};
}
return 0;
}
@ -357,13 +357,13 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
"cannot identify the cable type: adc(0x%x)\n",
adc);
return -EINVAL;
};
}
break;
default:
dev_err(info->dev,
"failed to identify the cable type: adc(0x%x)\n", adc);
return -EINVAL;
};
}
return cable_type;
}
@ -405,7 +405,7 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
dev_dbg(info->dev,
"cannot handle this cable_type (0x%x)\n", cable_type);
return 0;
};
}
/* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */
ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached);

View File

@ -163,8 +163,15 @@ static int coreboot_table_probe(struct platform_device *pdev)
return ret;
}
static int __cb_dev_unregister(struct device *dev, void *dummy)
{
device_unregister(dev);
return 0;
}
static int coreboot_table_remove(struct platform_device *pdev)
{
bus_for_each_dev(&coreboot_bus_type, NULL, NULL, __cb_dev_unregister);
bus_unregister(&coreboot_bus_type);
return 0;
}

View File

@ -76,6 +76,7 @@
#define GSMI_CMD_LOG_S0IX_RESUME 0x0b
#define GSMI_CMD_CLEAR_CONFIG 0x20
#define GSMI_CMD_HANDSHAKE_TYPE 0xC1
#define GSMI_CMD_RESERVED 0xff
/* Magic entry type for kernel events */
#define GSMI_LOG_ENTRY_TYPE_KERNEL 0xDEAD
@ -746,6 +747,7 @@ MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table);
static __init int gsmi_system_valid(void)
{
u32 hash;
u16 cmd, result;
if (!dmi_check_system(gsmi_dmi_table))
return -ENODEV;
@ -780,6 +782,23 @@ static __init int gsmi_system_valid(void)
return -ENODEV;
}
/* Test the smihandler with a bogus command. If it leaves the
* calling argument in %ax untouched, there is no handler for
* GSMI commands.
*/
cmd = GSMI_CALLBACK | GSMI_CMD_RESERVED << 8;
asm volatile (
"outb %%al, %%dx\n\t"
: "=a" (result)
: "0" (cmd),
"d" (acpi_gbl_FADT.smi_command)
: "memory", "cc"
);
if (cmd == result) {
pr_info("gsmi: no gsmi handler in firmware\n");
return -ENODEV;
}
/* Found */
return 0;
}
@ -1016,6 +1035,9 @@ static __init int gsmi_init(void)
dma_pool_destroy(gsmi_dev.dma_pool);
platform_device_unregister(gsmi_dev.pdev);
pr_info("gsmi: failed to load: %d\n", ret);
#ifdef CONFIG_PM
platform_driver_unregister(&gsmi_driver_info);
#endif
return ret;
}
@ -1037,6 +1059,9 @@ static void __exit gsmi_exit(void)
gsmi_buf_free(gsmi_dev.name_buf);
dma_pool_destroy(gsmi_dev.dma_pool);
platform_device_unregister(gsmi_dev.pdev);
#ifdef CONFIG_PM
platform_driver_unregister(&gsmi_driver_info);
#endif
}
module_init(gsmi_init);

View File

@ -268,7 +268,7 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl,
*/
msleep(1000);
count_in_sec--;
};
}
if (res.a0 == INTEL_SIP_SMC_STATUS_OK && count_in_sec)
cb_data->status = BIT(SVC_STATUS_RECONFIG_COMPLETED);
@ -512,7 +512,7 @@ static int svc_normal_to_secure_thread(void *data)
break;
}
};
}
kfree(cbdata);
kfree(pdata);

View File

@ -813,10 +813,8 @@ static int afu_dev_init(struct platform_device *pdev)
static int afu_dev_destroy(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_afu *afu;
mutex_lock(&pdata->lock);
afu = dfl_fpga_pdata_get_private(pdata);
afu_mmio_region_destroy(pdata);
afu_dma_region_destroy(pdata);
dfl_fpga_pdata_set_private(pdata, NULL);

View File

@ -675,10 +675,8 @@ static int fme_dev_init(struct platform_device *pdev)
static void fme_dev_destroy(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_fme *fme;
mutex_lock(&pdata->lock);
fme = dfl_fpga_pdata_get_private(pdata);
dfl_fpga_pdata_set_private(pdata, NULL);
mutex_unlock(&pdata->lock);
}

View File

@ -119,10 +119,8 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io_base = devm_ioremap_resource(kdev, res);
if (IS_ERR(priv->io_base)) {
dev_err(kdev, "unable to remap registers\n");
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
}
mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
&ts73xx_fpga_ops, priv);

View File

@ -101,7 +101,8 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
priv->clk = devm_clk_get(&pdev->dev, "aclk");
if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "input clock not found\n");
if (PTR_ERR(priv->clk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "input clock not found\n");
return PTR_ERR(priv->clk);
}

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS_core.o := -I$(src)
icc-core-objs := core.o
obj-$(CONFIG_INTERCONNECT) += icc-core.o

View File

@ -19,45 +19,22 @@
#include <linux/of.h>
#include <linux/overflow.h>
#include "internal.h"
#define CREATE_TRACE_POINTS
#include "trace.h"
static DEFINE_IDR(icc_idr);
static LIST_HEAD(icc_providers);
static DEFINE_MUTEX(icc_lock);
static struct dentry *icc_debugfs_dir;
/**
* struct icc_req - constraints that are attached to each node
* @req_node: entry in list of requests for the particular @node
* @node: the interconnect node to which this constraint applies
* @dev: reference to the device that sets the constraints
* @tag: path tag (optional)
* @avg_bw: an integer describing the average bandwidth in kBps
* @peak_bw: an integer describing the peak bandwidth in kBps
*/
struct icc_req {
struct hlist_node req_node;
struct icc_node *node;
struct device *dev;
u32 tag;
u32 avg_bw;
u32 peak_bw;
};
/**
* struct icc_path - interconnect path structure
* @num_nodes: number of hops (nodes)
* @reqs: array of the requests applicable to this path of nodes
*/
struct icc_path {
size_t num_nodes;
struct icc_req reqs[];
};
static void icc_summary_show_one(struct seq_file *s, struct icc_node *n)
{
if (!n)
return;
seq_printf(s, "%-30s %12u %12u\n",
seq_printf(s, "%-42s %12u %12u\n",
n->name, n->avg_bw, n->peak_bw);
}
@ -65,8 +42,8 @@ static int icc_summary_show(struct seq_file *s, void *data)
{
struct icc_provider *provider;
seq_puts(s, " node avg peak\n");
seq_puts(s, "--------------------------------------------------------\n");
seq_puts(s, " node tag avg peak\n");
seq_puts(s, "--------------------------------------------------------------------\n");
mutex_lock(&icc_lock);
@ -81,8 +58,8 @@ static int icc_summary_show(struct seq_file *s, void *data)
if (!r->dev)
continue;
seq_printf(s, " %-26s %12u %12u\n",
dev_name(r->dev), r->avg_bw,
seq_printf(s, " %-27s %12u %12u %12u\n",
dev_name(r->dev), r->tag, r->avg_bw,
r->peak_bw);
}
}
@ -94,6 +71,70 @@ static int icc_summary_show(struct seq_file *s, void *data)
}
DEFINE_SHOW_ATTRIBUTE(icc_summary);
static void icc_graph_show_link(struct seq_file *s, int level,
struct icc_node *n, struct icc_node *m)
{
seq_printf(s, "%s\"%d:%s\" -> \"%d:%s\"\n",
level == 2 ? "\t\t" : "\t",
n->id, n->name, m->id, m->name);
}
static void icc_graph_show_node(struct seq_file *s, struct icc_node *n)
{
seq_printf(s, "\t\t\"%d:%s\" [label=\"%d:%s",
n->id, n->name, n->id, n->name);
seq_printf(s, "\n\t\t\t|avg_bw=%ukBps", n->avg_bw);
seq_printf(s, "\n\t\t\t|peak_bw=%ukBps", n->peak_bw);
seq_puts(s, "\"]\n");
}
static int icc_graph_show(struct seq_file *s, void *data)
{
struct icc_provider *provider;
struct icc_node *n;
int cluster_index = 0;
int i;
seq_puts(s, "digraph {\n\trankdir = LR\n\tnode [shape = record]\n");
mutex_lock(&icc_lock);
/* draw providers as cluster subgraphs */
cluster_index = 0;
list_for_each_entry(provider, &icc_providers, provider_list) {
seq_printf(s, "\tsubgraph cluster_%d {\n", ++cluster_index);
if (provider->dev)
seq_printf(s, "\t\tlabel = \"%s\"\n",
dev_name(provider->dev));
/* draw nodes */
list_for_each_entry(n, &provider->nodes, node_list)
icc_graph_show_node(s, n);
/* draw internal links */
list_for_each_entry(n, &provider->nodes, node_list)
for (i = 0; i < n->num_links; ++i)
if (n->provider == n->links[i]->provider)
icc_graph_show_link(s, 2, n,
n->links[i]);
seq_puts(s, "\t}\n");
}
/* draw external links */
list_for_each_entry(provider, &icc_providers, provider_list)
list_for_each_entry(n, &provider->nodes, node_list)
for (i = 0; i < n->num_links; ++i)
if (n->provider != n->links[i]->provider)
icc_graph_show_link(s, 1, n,
n->links[i]);
mutex_unlock(&icc_lock);
seq_puts(s, "}");
return 0;
}
DEFINE_SHOW_ATTRIBUTE(icc_graph);
static struct icc_node *node_find(const int id)
{
return idr_find(&icc_idr, id);
@ -244,6 +285,16 @@ static int apply_constraints(struct icc_path *path)
return ret;
}
int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
*agg_avg += avg_bw;
*agg_peak = max(*agg_peak, peak_bw);
return 0;
}
EXPORT_SYMBOL_GPL(icc_std_aggregate);
/* of_icc_xlate_onecell() - Translate function using a single index.
* @spec: OF phandle args to map into an interconnect node.
* @data: private data (pointer to struct icc_onecell_data)
@ -382,9 +433,17 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
mutex_lock(&icc_lock);
path = path_find(dev, src_node, dst_node);
if (IS_ERR(path))
dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
mutex_unlock(&icc_lock);
if (IS_ERR(path)) {
dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
return path;
}
if (name)
path->name = kstrdup_const(name, GFP_KERNEL);
else
path->name = kasprintf(GFP_KERNEL, "%s-%s",
src_node->name, dst_node->name);
return path;
}
@ -436,9 +495,12 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
size_t i;
int ret;
if (!path || !path->num_nodes)
if (!path)
return 0;
if (WARN_ON(IS_ERR(path) || !path->num_nodes))
return -EINVAL;
mutex_lock(&icc_lock);
old_avg = path->reqs[0].avg_bw;
@ -453,6 +515,8 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
/* aggregate requests for this node */
aggregate_requests(node);
trace_icc_set_bw(path, node, i, avg_bw, peak_bw);
}
ret = apply_constraints(path);
@ -471,6 +535,8 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
mutex_unlock(&icc_lock);
trace_icc_set_bw_end(path, ret);
return ret;
}
EXPORT_SYMBOL_GPL(icc_set_bw);
@ -507,9 +573,12 @@ struct icc_path *icc_get(struct device *dev, const int src_id, const int dst_id)
goto out;
path = path_find(dev, src, dst);
if (IS_ERR(path))
if (IS_ERR(path)) {
dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
goto out;
}
path->name = kasprintf(GFP_KERNEL, "%s-%s", src->name, dst->name);
out:
mutex_unlock(&icc_lock);
return path;
@ -545,6 +614,7 @@ void icc_put(struct icc_path *path)
}
mutex_unlock(&icc_lock);
kfree_const(path->name);
kfree(path);
}
EXPORT_SYMBOL_GPL(icc_put);
@ -742,6 +812,28 @@ void icc_node_del(struct icc_node *node)
}
EXPORT_SYMBOL_GPL(icc_node_del);
/**
* icc_nodes_remove() - remove all previously added nodes from provider
* @provider: the interconnect provider we are removing nodes from
*
* Return: 0 on success, or an error code otherwise
*/
int icc_nodes_remove(struct icc_provider *provider)
{
struct icc_node *n, *tmp;
if (WARN_ON(IS_ERR_OR_NULL(provider)))
return -EINVAL;
list_for_each_entry_safe_reverse(n, tmp, &provider->nodes, node_list) {
icc_node_del(n);
icc_node_destroy(n->id);
}
return 0;
}
EXPORT_SYMBOL_GPL(icc_nodes_remove);
/**
* icc_provider_add() - add a new interconnect provider
* @provider: the interconnect provider that will be added into topology
@ -802,6 +894,8 @@ static int __init icc_init(void)
icc_debugfs_dir = debugfs_create_dir("interconnect", NULL);
debugfs_create_file("interconnect_summary", 0444,
icc_debugfs_dir, NULL, &icc_summary_fops);
debugfs_create_file("interconnect_graph", 0444,
icc_debugfs_dir, NULL, &icc_graph_fops);
return 0;
}

View File

@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework internal structs
*
* Copyright (c) 2019, Linaro Ltd.
* Author: Georgi Djakov <georgi.djakov@linaro.org>
*/
#ifndef __DRIVERS_INTERCONNECT_INTERNAL_H
#define __DRIVERS_INTERCONNECT_INTERNAL_H
/**
* struct icc_req - constraints that are attached to each node
* @req_node: entry in list of requests for the particular @node
* @node: the interconnect node to which this constraint applies
* @dev: reference to the device that sets the constraints
* @tag: path tag (optional)
* @avg_bw: an integer describing the average bandwidth in kBps
* @peak_bw: an integer describing the peak bandwidth in kBps
*/
struct icc_req {
struct hlist_node req_node;
struct icc_node *node;
struct device *dev;
u32 tag;
u32 avg_bw;
u32 peak_bw;
};
/**
* struct icc_path - interconnect path structure
* @name: a string name of the path (useful for ftrace)
* @num_nodes: number of hops (nodes)
* @reqs: array of the requests applicable to this path of nodes
*/
struct icc_path {
const char *name;
size_t num_nodes;
struct icc_req reqs[];
};
#endif

View File

@ -5,6 +5,15 @@ config INTERCONNECT_QCOM
help
Support for Qualcomm's Network-on-Chip interconnect hardware.
config INTERCONNECT_QCOM_MSM8916
tristate "Qualcomm MSM8916 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8916-based
platforms.
config INTERCONNECT_QCOM_MSM8974
tristate "Qualcomm MSM8974 interconnect driver"
depends on INTERCONNECT_QCOM

View File

@ -1,10 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
qnoc-msm8916-objs := msm8916.o
qnoc-msm8974-objs := msm8974.o
qnoc-qcs404-objs := qcs404.o
qnoc-sdm845-objs := sdm845.o
icc-smd-rpm-objs := smd-rpm.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o

View File

@ -0,0 +1,554 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018-2020 Linaro Ltd
* Author: Georgi Djakov <georgi.djakov@linaro.org>
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <dt-bindings/interconnect/qcom,msm8916.h>
#include "smd-rpm.h"
#define RPM_BUS_MASTER_REQ 0x73616d62
#define RPM_BUS_SLAVE_REQ 0x766c7362
enum {
MSM8916_BIMC_SNOC_MAS = 1,
MSM8916_BIMC_SNOC_SLV,
MSM8916_MASTER_AMPSS_M0,
MSM8916_MASTER_LPASS,
MSM8916_MASTER_BLSP_1,
MSM8916_MASTER_DEHR,
MSM8916_MASTER_GRAPHICS_3D,
MSM8916_MASTER_JPEG,
MSM8916_MASTER_MDP_PORT0,
MSM8916_MASTER_CRYPTO_CORE0,
MSM8916_MASTER_SDCC_1,
MSM8916_MASTER_SDCC_2,
MSM8916_MASTER_QDSS_BAM,
MSM8916_MASTER_QDSS_ETR,
MSM8916_MASTER_SNOC_CFG,
MSM8916_MASTER_SPDM,
MSM8916_MASTER_TCU0,
MSM8916_MASTER_TCU1,
MSM8916_MASTER_USB_HS,
MSM8916_MASTER_VFE,
MSM8916_MASTER_VIDEO_P0,
MSM8916_SNOC_MM_INT_0,
MSM8916_SNOC_MM_INT_1,
MSM8916_SNOC_MM_INT_2,
MSM8916_SNOC_MM_INT_BIMC,
MSM8916_PNOC_INT_0,
MSM8916_PNOC_INT_1,
MSM8916_PNOC_MAS_0,
MSM8916_PNOC_MAS_1,
MSM8916_PNOC_SLV_0,
MSM8916_PNOC_SLV_1,
MSM8916_PNOC_SLV_2,
MSM8916_PNOC_SLV_3,
MSM8916_PNOC_SLV_4,
MSM8916_PNOC_SLV_8,
MSM8916_PNOC_SLV_9,
MSM8916_PNOC_SNOC_MAS,
MSM8916_PNOC_SNOC_SLV,
MSM8916_SNOC_QDSS_INT,
MSM8916_SLAVE_AMPSS_L2,
MSM8916_SLAVE_APSS,
MSM8916_SLAVE_LPASS,
MSM8916_SLAVE_BIMC_CFG,
MSM8916_SLAVE_BLSP_1,
MSM8916_SLAVE_BOOT_ROM,
MSM8916_SLAVE_CAMERA_CFG,
MSM8916_SLAVE_CATS_128,
MSM8916_SLAVE_OCMEM_64,
MSM8916_SLAVE_CLK_CTL,
MSM8916_SLAVE_CRYPTO_0_CFG,
MSM8916_SLAVE_DEHR_CFG,
MSM8916_SLAVE_DISPLAY_CFG,
MSM8916_SLAVE_EBI_CH0,
MSM8916_SLAVE_GRAPHICS_3D_CFG,
MSM8916_SLAVE_IMEM_CFG,
MSM8916_SLAVE_IMEM,
MSM8916_SLAVE_MPM,
MSM8916_SLAVE_MSG_RAM,
MSM8916_SLAVE_MSS,
MSM8916_SLAVE_PDM,
MSM8916_SLAVE_PMIC_ARB,
MSM8916_SLAVE_PNOC_CFG,
MSM8916_SLAVE_PRNG,
MSM8916_SLAVE_QDSS_CFG,
MSM8916_SLAVE_QDSS_STM,
MSM8916_SLAVE_RBCPR_CFG,
MSM8916_SLAVE_SDCC_1,
MSM8916_SLAVE_SDCC_2,
MSM8916_SLAVE_SECURITY,
MSM8916_SLAVE_SNOC_CFG,
MSM8916_SLAVE_SPDM,
MSM8916_SLAVE_SRVC_SNOC,
MSM8916_SLAVE_TCSR,
MSM8916_SLAVE_TLMM,
MSM8916_SLAVE_USB_HS,
MSM8916_SLAVE_VENUS_CFG,
MSM8916_SNOC_BIMC_0_MAS,
MSM8916_SNOC_BIMC_0_SLV,
MSM8916_SNOC_BIMC_1_MAS,
MSM8916_SNOC_BIMC_1_SLV,
MSM8916_SNOC_INT_0,
MSM8916_SNOC_INT_1,
MSM8916_SNOC_INT_BIMC,
MSM8916_SNOC_PNOC_MAS,
MSM8916_SNOC_PNOC_SLV,
};
#define to_msm8916_provider(_provider) \
container_of(_provider, struct msm8916_icc_provider, provider)
static const struct clk_bulk_data msm8916_bus_clocks[] = {
{ .id = "bus" },
{ .id = "bus_a" },
};
/**
* struct msm8916_icc_provider - Qualcomm specific interconnect provider
* @provider: generic interconnect provider
* @bus_clks: the clk_bulk_data table of bus clocks
* @num_clks: the total number of clk_bulk_data entries
*/
struct msm8916_icc_provider {
struct icc_provider provider;
struct clk_bulk_data *bus_clks;
int num_clks;
};
#define MSM8916_MAX_LINKS 8
/**
* struct msm8916_icc_node - Qualcomm specific interconnect nodes
* @name: the node name used in debugfs
* @id: a unique node identifier
* @links: an array of nodes where we can go next while traversing
* @num_links: the total number of @links
* @buswidth: width of the interconnect between a node and the bus (bytes)
* @mas_rpm_id: RPM ID for devices that are bus masters
* @slv_rpm_id: RPM ID for devices that are bus slaves
* @rate: current bus clock rate in Hz
*/
struct msm8916_icc_node {
unsigned char *name;
u16 id;
u16 links[MSM8916_MAX_LINKS];
u16 num_links;
u16 buswidth;
int mas_rpm_id;
int slv_rpm_id;
u64 rate;
};
struct msm8916_icc_desc {
struct msm8916_icc_node **nodes;
size_t num_nodes;
};
#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \
...) \
static struct msm8916_icc_node _name = { \
.name = #_name, \
.id = _id, \
.buswidth = _buswidth, \
.mas_rpm_id = _mas_rpm_id, \
.slv_rpm_id = _slv_rpm_id, \
.num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \
.links = { __VA_ARGS__ }, \
}
DEFINE_QNODE(bimc_snoc_mas, MSM8916_BIMC_SNOC_MAS, 8, -1, -1, MSM8916_BIMC_SNOC_SLV);
DEFINE_QNODE(bimc_snoc_slv, MSM8916_BIMC_SNOC_SLV, 8, -1, -1, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_1);
DEFINE_QNODE(mas_apss, MSM8916_MASTER_AMPSS_M0, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2);
DEFINE_QNODE(mas_audio, MSM8916_MASTER_LPASS, 4, -1, -1, MSM8916_PNOC_MAS_0);
DEFINE_QNODE(mas_blsp_1, MSM8916_MASTER_BLSP_1, 4, -1, -1, MSM8916_PNOC_MAS_1);
DEFINE_QNODE(mas_dehr, MSM8916_MASTER_DEHR, 4, -1, -1, MSM8916_PNOC_MAS_0);
DEFINE_QNODE(mas_gfx, MSM8916_MASTER_GRAPHICS_3D, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2);
DEFINE_QNODE(mas_jpeg, MSM8916_MASTER_JPEG, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2);
DEFINE_QNODE(mas_mdp, MSM8916_MASTER_MDP_PORT0, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2);
DEFINE_QNODE(mas_pcnoc_crypto_0, MSM8916_MASTER_CRYPTO_CORE0, 8, -1, -1, MSM8916_PNOC_INT_1);
DEFINE_QNODE(mas_pcnoc_sdcc_1, MSM8916_MASTER_SDCC_1, 8, -1, -1, MSM8916_PNOC_INT_1);
DEFINE_QNODE(mas_pcnoc_sdcc_2, MSM8916_MASTER_SDCC_2, 8, -1, -1, MSM8916_PNOC_INT_1);
DEFINE_QNODE(mas_qdss_bam, MSM8916_MASTER_QDSS_BAM, 8, -1, -1, MSM8916_SNOC_QDSS_INT);
DEFINE_QNODE(mas_qdss_etr, MSM8916_MASTER_QDSS_ETR, 8, -1, -1, MSM8916_SNOC_QDSS_INT);
DEFINE_QNODE(mas_snoc_cfg, MSM8916_MASTER_SNOC_CFG, 4, 20, -1, MSM8916_SNOC_QDSS_INT);
DEFINE_QNODE(mas_spdm, MSM8916_MASTER_SPDM, 4, -1, -1, MSM8916_PNOC_MAS_0);
DEFINE_QNODE(mas_tcu0, MSM8916_MASTER_TCU0, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2);
DEFINE_QNODE(mas_tcu1, MSM8916_MASTER_TCU1, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2);
DEFINE_QNODE(mas_usb_hs, MSM8916_MASTER_USB_HS, 4, -1, -1, MSM8916_PNOC_MAS_1);
DEFINE_QNODE(mas_vfe, MSM8916_MASTER_VFE, 16, -1, -1, MSM8916_SNOC_MM_INT_1, MSM8916_SNOC_MM_INT_2);
DEFINE_QNODE(mas_video, MSM8916_MASTER_VIDEO_P0, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2);
DEFINE_QNODE(mm_int_0, MSM8916_SNOC_MM_INT_0, 16, -1, -1, MSM8916_SNOC_MM_INT_BIMC);
DEFINE_QNODE(mm_int_1, MSM8916_SNOC_MM_INT_1, 16, -1, -1, MSM8916_SNOC_MM_INT_BIMC);
DEFINE_QNODE(mm_int_2, MSM8916_SNOC_MM_INT_2, 16, -1, -1, MSM8916_SNOC_INT_0);
DEFINE_QNODE(mm_int_bimc, MSM8916_SNOC_MM_INT_BIMC, 16, -1, -1, MSM8916_SNOC_BIMC_1_MAS);
DEFINE_QNODE(pcnoc_int_0, MSM8916_PNOC_INT_0, 8, -1, -1, MSM8916_PNOC_SNOC_MAS, MSM8916_PNOC_SLV_0, MSM8916_PNOC_SLV_1, MSM8916_PNOC_SLV_2, MSM8916_PNOC_SLV_3, MSM8916_PNOC_SLV_4, MSM8916_PNOC_SLV_8, MSM8916_PNOC_SLV_9);
DEFINE_QNODE(pcnoc_int_1, MSM8916_PNOC_INT_1, 8, -1, -1, MSM8916_PNOC_SNOC_MAS);
DEFINE_QNODE(pcnoc_m_0, MSM8916_PNOC_MAS_0, 8, -1, -1, MSM8916_PNOC_INT_0);
DEFINE_QNODE(pcnoc_m_1, MSM8916_PNOC_MAS_1, 8, -1, -1, MSM8916_PNOC_SNOC_MAS);
DEFINE_QNODE(pcnoc_s_0, MSM8916_PNOC_SLV_0, 8, -1, -1, MSM8916_SLAVE_CLK_CTL, MSM8916_SLAVE_TLMM, MSM8916_SLAVE_TCSR, MSM8916_SLAVE_SECURITY, MSM8916_SLAVE_MSS);
DEFINE_QNODE(pcnoc_s_1, MSM8916_PNOC_SLV_1, 8, -1, -1, MSM8916_SLAVE_IMEM_CFG, MSM8916_SLAVE_CRYPTO_0_CFG, MSM8916_SLAVE_MSG_RAM, MSM8916_SLAVE_PDM, MSM8916_SLAVE_PRNG);
DEFINE_QNODE(pcnoc_s_2, MSM8916_PNOC_SLV_2, 8, -1, -1, MSM8916_SLAVE_SPDM, MSM8916_SLAVE_BOOT_ROM, MSM8916_SLAVE_BIMC_CFG, MSM8916_SLAVE_PNOC_CFG, MSM8916_SLAVE_PMIC_ARB);
DEFINE_QNODE(pcnoc_s_3, MSM8916_PNOC_SLV_3, 8, -1, -1, MSM8916_SLAVE_MPM, MSM8916_SLAVE_SNOC_CFG, MSM8916_SLAVE_RBCPR_CFG, MSM8916_SLAVE_QDSS_CFG, MSM8916_SLAVE_DEHR_CFG);
DEFINE_QNODE(pcnoc_s_4, MSM8916_PNOC_SLV_4, 8, -1, -1, MSM8916_SLAVE_VENUS_CFG, MSM8916_SLAVE_CAMERA_CFG, MSM8916_SLAVE_DISPLAY_CFG);
DEFINE_QNODE(pcnoc_s_8, MSM8916_PNOC_SLV_8, 8, -1, -1, MSM8916_SLAVE_USB_HS, MSM8916_SLAVE_SDCC_1, MSM8916_SLAVE_BLSP_1);
DEFINE_QNODE(pcnoc_s_9, MSM8916_PNOC_SLV_9, 8, -1, -1, MSM8916_SLAVE_SDCC_2, MSM8916_SLAVE_LPASS, MSM8916_SLAVE_GRAPHICS_3D_CFG);
DEFINE_QNODE(pcnoc_snoc_mas, MSM8916_PNOC_SNOC_MAS, 8, 29, -1, MSM8916_PNOC_SNOC_SLV);
DEFINE_QNODE(pcnoc_snoc_slv, MSM8916_PNOC_SNOC_SLV, 8, -1, 45, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_BIMC, MSM8916_SNOC_INT_1);
DEFINE_QNODE(qdss_int, MSM8916_SNOC_QDSS_INT, 8, -1, -1, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_BIMC);
DEFINE_QNODE(slv_apps_l2, MSM8916_SLAVE_AMPSS_L2, 8, -1, -1, 0);
DEFINE_QNODE(slv_apss, MSM8916_SLAVE_APSS, 4, -1, 20, 0);
DEFINE_QNODE(slv_audio, MSM8916_SLAVE_LPASS, 4, -1, -1, 0);
DEFINE_QNODE(slv_bimc_cfg, MSM8916_SLAVE_BIMC_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_blsp_1, MSM8916_SLAVE_BLSP_1, 4, -1, -1, 0);
DEFINE_QNODE(slv_boot_rom, MSM8916_SLAVE_BOOT_ROM, 4, -1, -1, 0);
DEFINE_QNODE(slv_camera_cfg, MSM8916_SLAVE_CAMERA_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_cats_0, MSM8916_SLAVE_CATS_128, 16, -1, 106, 0);
DEFINE_QNODE(slv_cats_1, MSM8916_SLAVE_OCMEM_64, 8, -1, 107, 0);
DEFINE_QNODE(slv_clk_ctl, MSM8916_SLAVE_CLK_CTL, 4, -1, -1, 0);
DEFINE_QNODE(slv_crypto_0_cfg, MSM8916_SLAVE_CRYPTO_0_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_dehr_cfg, MSM8916_SLAVE_DEHR_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_display_cfg, MSM8916_SLAVE_DISPLAY_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_ebi_ch0, MSM8916_SLAVE_EBI_CH0, 8, -1, 0, 0);
DEFINE_QNODE(slv_gfx_cfg, MSM8916_SLAVE_GRAPHICS_3D_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_imem_cfg, MSM8916_SLAVE_IMEM_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_imem, MSM8916_SLAVE_IMEM, 8, -1, 26, 0);
DEFINE_QNODE(slv_mpm, MSM8916_SLAVE_MPM, 4, -1, -1, 0);
DEFINE_QNODE(slv_msg_ram, MSM8916_SLAVE_MSG_RAM, 4, -1, -1, 0);
DEFINE_QNODE(slv_mss, MSM8916_SLAVE_MSS, 4, -1, -1, 0);
DEFINE_QNODE(slv_pdm, MSM8916_SLAVE_PDM, 4, -1, -1, 0);
DEFINE_QNODE(slv_pmic_arb, MSM8916_SLAVE_PMIC_ARB, 4, -1, -1, 0);
DEFINE_QNODE(slv_pcnoc_cfg, MSM8916_SLAVE_PNOC_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_prng, MSM8916_SLAVE_PRNG, 4, -1, -1, 0);
DEFINE_QNODE(slv_qdss_cfg, MSM8916_SLAVE_QDSS_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_qdss_stm, MSM8916_SLAVE_QDSS_STM, 4, -1, 30, 0);
DEFINE_QNODE(slv_rbcpr_cfg, MSM8916_SLAVE_RBCPR_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_sdcc_1, MSM8916_SLAVE_SDCC_1, 4, -1, -1, 0);
DEFINE_QNODE(slv_sdcc_2, MSM8916_SLAVE_SDCC_2, 4, -1, -1, 0);
DEFINE_QNODE(slv_security, MSM8916_SLAVE_SECURITY, 4, -1, -1, 0);
DEFINE_QNODE(slv_snoc_cfg, MSM8916_SLAVE_SNOC_CFG, 4, -1, -1, 0);
DEFINE_QNODE(slv_spdm, MSM8916_SLAVE_SPDM, 4, -1, -1, 0);
DEFINE_QNODE(slv_srvc_snoc, MSM8916_SLAVE_SRVC_SNOC, 8, -1, 29, 0);
DEFINE_QNODE(slv_tcsr, MSM8916_SLAVE_TCSR, 4, -1, -1, 0);
DEFINE_QNODE(slv_tlmm, MSM8916_SLAVE_TLMM, 4, -1, -1, 0);
DEFINE_QNODE(slv_usb_hs, MSM8916_SLAVE_USB_HS, 4, -1, -1, 0);
DEFINE_QNODE(slv_venus_cfg, MSM8916_SLAVE_VENUS_CFG, 4, -1, -1, 0);
DEFINE_QNODE(snoc_bimc_0_mas, MSM8916_SNOC_BIMC_0_MAS, 8, 3, -1, MSM8916_SNOC_BIMC_0_SLV);
DEFINE_QNODE(snoc_bimc_0_slv, MSM8916_SNOC_BIMC_0_SLV, 8, -1, 24, MSM8916_SLAVE_EBI_CH0);
DEFINE_QNODE(snoc_bimc_1_mas, MSM8916_SNOC_BIMC_1_MAS, 16, -1, -1, MSM8916_SNOC_BIMC_1_SLV);
DEFINE_QNODE(snoc_bimc_1_slv, MSM8916_SNOC_BIMC_1_SLV, 8, -1, -1, MSM8916_SLAVE_EBI_CH0);
DEFINE_QNODE(snoc_int_0, MSM8916_SNOC_INT_0, 8, 99, 130, MSM8916_SLAVE_QDSS_STM, MSM8916_SLAVE_IMEM, MSM8916_SNOC_PNOC_MAS);
DEFINE_QNODE(snoc_int_1, MSM8916_SNOC_INT_1, 8, 100, 131, MSM8916_SLAVE_APSS, MSM8916_SLAVE_CATS_128, MSM8916_SLAVE_OCMEM_64);
DEFINE_QNODE(snoc_int_bimc, MSM8916_SNOC_INT_BIMC, 8, 101, 132, MSM8916_SNOC_BIMC_0_MAS);
DEFINE_QNODE(snoc_pcnoc_mas, MSM8916_SNOC_PNOC_MAS, 8, -1, -1, MSM8916_SNOC_PNOC_SLV);
DEFINE_QNODE(snoc_pcnoc_slv, MSM8916_SNOC_PNOC_SLV, 8, -1, -1, MSM8916_PNOC_INT_0);
static struct msm8916_icc_node *msm8916_snoc_nodes[] = {
[BIMC_SNOC_SLV] = &bimc_snoc_slv,
[MASTER_JPEG] = &mas_jpeg,
[MASTER_MDP_PORT0] = &mas_mdp,
[MASTER_QDSS_BAM] = &mas_qdss_bam,
[MASTER_QDSS_ETR] = &mas_qdss_etr,
[MASTER_SNOC_CFG] = &mas_snoc_cfg,
[MASTER_VFE] = &mas_vfe,
[MASTER_VIDEO_P0] = &mas_video,
[SNOC_MM_INT_0] = &mm_int_0,
[SNOC_MM_INT_1] = &mm_int_1,
[SNOC_MM_INT_2] = &mm_int_2,
[SNOC_MM_INT_BIMC] = &mm_int_bimc,
[PCNOC_SNOC_SLV] = &pcnoc_snoc_slv,
[SLAVE_APSS] = &slv_apss,
[SLAVE_CATS_128] = &slv_cats_0,
[SLAVE_OCMEM_64] = &slv_cats_1,
[SLAVE_IMEM] = &slv_imem,
[SLAVE_QDSS_STM] = &slv_qdss_stm,
[SLAVE_SRVC_SNOC] = &slv_srvc_snoc,
[SNOC_BIMC_0_MAS] = &snoc_bimc_0_mas,
[SNOC_BIMC_1_MAS] = &snoc_bimc_1_mas,
[SNOC_INT_0] = &snoc_int_0,
[SNOC_INT_1] = &snoc_int_1,
[SNOC_INT_BIMC] = &snoc_int_bimc,
[SNOC_PCNOC_MAS] = &snoc_pcnoc_mas,
[SNOC_QDSS_INT] = &qdss_int,
};
static struct msm8916_icc_desc msm8916_snoc = {
.nodes = msm8916_snoc_nodes,
.num_nodes = ARRAY_SIZE(msm8916_snoc_nodes),
};
static struct msm8916_icc_node *msm8916_bimc_nodes[] = {
[BIMC_SNOC_MAS] = &bimc_snoc_mas,
[MASTER_AMPSS_M0] = &mas_apss,
[MASTER_GRAPHICS_3D] = &mas_gfx,
[MASTER_TCU0] = &mas_tcu0,
[MASTER_TCU1] = &mas_tcu1,
[SLAVE_AMPSS_L2] = &slv_apps_l2,
[SLAVE_EBI_CH0] = &slv_ebi_ch0,
[SNOC_BIMC_0_SLV] = &snoc_bimc_0_slv,
[SNOC_BIMC_1_SLV] = &snoc_bimc_1_slv,
};
static struct msm8916_icc_desc msm8916_bimc = {
.nodes = msm8916_bimc_nodes,
.num_nodes = ARRAY_SIZE(msm8916_bimc_nodes),
};
static struct msm8916_icc_node *msm8916_pcnoc_nodes[] = {
[MASTER_BLSP_1] = &mas_blsp_1,
[MASTER_DEHR] = &mas_dehr,
[MASTER_LPASS] = &mas_audio,
[MASTER_CRYPTO_CORE0] = &mas_pcnoc_crypto_0,
[MASTER_SDCC_1] = &mas_pcnoc_sdcc_1,
[MASTER_SDCC_2] = &mas_pcnoc_sdcc_2,
[MASTER_SPDM] = &mas_spdm,
[MASTER_USB_HS] = &mas_usb_hs,
[PCNOC_INT_0] = &pcnoc_int_0,
[PCNOC_INT_1] = &pcnoc_int_1,
[PCNOC_MAS_0] = &pcnoc_m_0,
[PCNOC_MAS_1] = &pcnoc_m_1,
[PCNOC_SLV_0] = &pcnoc_s_0,
[PCNOC_SLV_1] = &pcnoc_s_1,
[PCNOC_SLV_2] = &pcnoc_s_2,
[PCNOC_SLV_3] = &pcnoc_s_3,
[PCNOC_SLV_4] = &pcnoc_s_4,
[PCNOC_SLV_8] = &pcnoc_s_8,
[PCNOC_SLV_9] = &pcnoc_s_9,
[PCNOC_SNOC_MAS] = &pcnoc_snoc_mas,
[SLAVE_BIMC_CFG] = &slv_bimc_cfg,
[SLAVE_BLSP_1] = &slv_blsp_1,
[SLAVE_BOOT_ROM] = &slv_boot_rom,
[SLAVE_CAMERA_CFG] = &slv_camera_cfg,
[SLAVE_CLK_CTL] = &slv_clk_ctl,
[SLAVE_CRYPTO_0_CFG] = &slv_crypto_0_cfg,
[SLAVE_DEHR_CFG] = &slv_dehr_cfg,
[SLAVE_DISPLAY_CFG] = &slv_display_cfg,
[SLAVE_GRAPHICS_3D_CFG] = &slv_gfx_cfg,
[SLAVE_IMEM_CFG] = &slv_imem_cfg,
[SLAVE_LPASS] = &slv_audio,
[SLAVE_MPM] = &slv_mpm,
[SLAVE_MSG_RAM] = &slv_msg_ram,
[SLAVE_MSS] = &slv_mss,
[SLAVE_PDM] = &slv_pdm,
[SLAVE_PMIC_ARB] = &slv_pmic_arb,
[SLAVE_PCNOC_CFG] = &slv_pcnoc_cfg,
[SLAVE_PRNG] = &slv_prng,
[SLAVE_QDSS_CFG] = &slv_qdss_cfg,
[SLAVE_RBCPR_CFG] = &slv_rbcpr_cfg,
[SLAVE_SDCC_1] = &slv_sdcc_1,
[SLAVE_SDCC_2] = &slv_sdcc_2,
[SLAVE_SECURITY] = &slv_security,
[SLAVE_SNOC_CFG] = &slv_snoc_cfg,
[SLAVE_SPDM] = &slv_spdm,
[SLAVE_TCSR] = &slv_tcsr,
[SLAVE_TLMM] = &slv_tlmm,
[SLAVE_USB_HS] = &slv_usb_hs,
[SLAVE_VENUS_CFG] = &slv_venus_cfg,
[SNOC_PCNOC_SLV] = &snoc_pcnoc_slv,
};
static struct msm8916_icc_desc msm8916_pcnoc = {
.nodes = msm8916_pcnoc_nodes,
.num_nodes = ARRAY_SIZE(msm8916_pcnoc_nodes),
};
static int msm8916_icc_set(struct icc_node *src, struct icc_node *dst)
{
struct msm8916_icc_provider *qp;
struct msm8916_icc_node *qn;
u64 sum_bw, max_peak_bw, rate;
u32 agg_avg = 0, agg_peak = 0;
struct icc_provider *provider;
struct icc_node *n;
int ret, i;
qn = src->data;
provider = src->provider;
qp = to_msm8916_provider(provider);
list_for_each_entry(n, &provider->nodes, node_list)
provider->aggregate(n, 0, n->avg_bw, n->peak_bw,
&agg_avg, &agg_peak);
sum_bw = icc_units_to_bps(agg_avg);
max_peak_bw = icc_units_to_bps(agg_peak);
/* send bandwidth request message to the RPM processor */
if (qn->mas_rpm_id != -1) {
ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
RPM_BUS_MASTER_REQ,
qn->mas_rpm_id,
sum_bw);
if (ret) {
pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
qn->mas_rpm_id, ret);
return ret;
}
}
if (qn->slv_rpm_id != -1) {
ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
RPM_BUS_SLAVE_REQ,
qn->slv_rpm_id,
sum_bw);
if (ret) {
pr_err("qcom_icc_rpm_smd_send slv error %d\n",
ret);
return ret;
}
}
rate = max(sum_bw, max_peak_bw);
do_div(rate, qn->buswidth);
if (qn->rate == rate)
return 0;
for (i = 0; i < qp->num_clks; i++) {
ret = clk_set_rate(qp->bus_clks[i].clk, rate);
if (ret) {
pr_err("%s clk_set_rate error: %d\n",
qp->bus_clks[i].id, ret);
return ret;
}
}
qn->rate = rate;
return 0;
}
static int msm8916_qnoc_probe(struct platform_device *pdev)
{
const struct msm8916_icc_desc *desc;
struct msm8916_icc_node **qnodes;
struct msm8916_icc_provider *qp;
struct device *dev = &pdev->dev;
struct icc_onecell_data *data;
struct icc_provider *provider;
struct icc_node *node;
size_t num_nodes, i;
int ret;
/* wait for the RPM proxy */
if (!qcom_icc_rpm_smd_available())
return -EPROBE_DEFER;
desc = of_device_get_match_data(dev);
if (!desc)
return -EINVAL;
qnodes = desc->nodes;
num_nodes = desc->num_nodes;
qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
if (!qp)
return -ENOMEM;
data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
GFP_KERNEL);
if (!data)
return -ENOMEM;
qp->bus_clks = devm_kmemdup(dev, msm8916_bus_clocks,
sizeof(msm8916_bus_clocks), GFP_KERNEL);
if (!qp->bus_clks)
return -ENOMEM;
qp->num_clks = ARRAY_SIZE(msm8916_bus_clocks);
ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks);
if (ret)
return ret;
ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks);
if (ret)
return ret;
provider = &qp->provider;
INIT_LIST_HEAD(&provider->nodes);
provider->dev = dev;
provider->set = msm8916_icc_set;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
provider->data = data;
ret = icc_provider_add(provider);
if (ret) {
dev_err(dev, "error adding interconnect provider: %d\n", ret);
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
return ret;
}
for (i = 0; i < num_nodes; i++) {
size_t j;
node = icc_node_create(qnodes[i]->id);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err;
}
node->name = qnodes[i]->name;
node->data = qnodes[i];
icc_node_add(node, provider);
for (j = 0; j < qnodes[i]->num_links; j++)
icc_link_create(node, qnodes[i]->links[j]);
data->nodes[i] = node;
}
data->num_nodes = num_nodes;
platform_set_drvdata(pdev, qp);
return 0;
err:
icc_nodes_remove(provider);
icc_provider_del(provider);
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
return ret;
}
static int msm8916_qnoc_remove(struct platform_device *pdev)
{
struct msm8916_icc_provider *qp = platform_get_drvdata(pdev);
icc_nodes_remove(&qp->provider);
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
return icc_provider_del(&qp->provider);
}
static const struct of_device_id msm8916_noc_of_match[] = {
{ .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc },
{ .compatible = "qcom,msm8916-pcnoc", .data = &msm8916_pcnoc },
{ .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc },
{ }
};
MODULE_DEVICE_TABLE(of, msm8916_noc_of_match);
static struct platform_driver msm8916_noc_driver = {
.probe = msm8916_qnoc_probe,
.remove = msm8916_qnoc_remove,
.driver = {
.name = "qnoc-msm8916",
.of_match_table = msm8916_noc_of_match,
},
};
module_platform_driver(msm8916_noc_driver);
MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>");
MODULE_DESCRIPTION("Qualcomm MSM8916 NoC driver");
MODULE_LICENSE("GPL v2");

View File

@ -550,15 +550,6 @@ static struct msm8974_icc_desc msm8974_snoc = {
.num_nodes = ARRAY_SIZE(msm8974_snoc_nodes),
};
static int msm8974_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
*agg_avg += avg_bw;
*agg_peak = max(*agg_peak, peak_bw);
return 0;
}
static void msm8974_icc_rpm_smd_send(struct device *dev, int rsc_type,
char *name, int id, u64 val)
{
@ -603,8 +594,8 @@ static int msm8974_icc_set(struct icc_node *src, struct icc_node *dst)
qp = to_msm8974_icc_provider(provider);
list_for_each_entry(n, &provider->nodes, node_list)
msm8974_icc_aggregate(n, 0, n->avg_bw, n->peak_bw,
&agg_avg, &agg_peak);
provider->aggregate(n, 0, n->avg_bw, n->peak_bw,
&agg_avg, &agg_peak);
sum_bw = icc_units_to_bps(agg_avg);
max_peak_bw = icc_units_to_bps(agg_peak);
@ -652,7 +643,7 @@ static int msm8974_icc_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct icc_onecell_data *data;
struct icc_provider *provider;
struct icc_node *node, *tmp;
struct icc_node *node;
size_t num_nodes, i;
int ret;
@ -694,7 +685,7 @@ static int msm8974_icc_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&provider->nodes);
provider->dev = dev;
provider->set = msm8974_icc_set;
provider->aggregate = msm8974_icc_aggregate;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
provider->data = data;
@ -732,10 +723,7 @@ static int msm8974_icc_probe(struct platform_device *pdev)
return 0;
err_del_icc:
list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) {
icc_node_del(node);
icc_node_destroy(node->id);
}
icc_nodes_remove(provider);
icc_provider_del(provider);
err_disable_clks:
@ -747,16 +735,10 @@ static int msm8974_icc_probe(struct platform_device *pdev)
static int msm8974_icc_remove(struct platform_device *pdev)
{
struct msm8974_icc_provider *qp = platform_get_drvdata(pdev);
struct icc_provider *provider = &qp->provider;
struct icc_node *n, *tmp;
list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) {
icc_node_del(n);
icc_node_destroy(n->id);
}
icc_nodes_remove(&qp->provider);
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
return icc_provider_del(provider);
return icc_provider_del(&qp->provider);
}
static const struct of_device_id msm8974_noc_of_match[] = {

View File

@ -327,15 +327,6 @@ static struct qcom_icc_desc qcs404_snoc = {
.num_nodes = ARRAY_SIZE(qcs404_snoc_nodes),
};
static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
*agg_avg += avg_bw;
*agg_peak = max(*agg_peak, peak_bw);
return 0;
}
static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
{
struct qcom_icc_provider *qp;
@ -354,8 +345,8 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
qp = to_qcom_provider(provider);
list_for_each_entry(n, &provider->nodes, node_list)
qcom_icc_aggregate(n, 0, n->avg_bw, n->peak_bw,
&agg_avg, &agg_peak);
provider->aggregate(n, 0, n->avg_bw, n->peak_bw,
&agg_avg, &agg_peak);
sum_bw = icc_units_to_bps(agg_avg);
max_peak_bw = icc_units_to_bps(agg_peak);
@ -414,7 +405,7 @@ static int qnoc_probe(struct platform_device *pdev)
struct icc_provider *provider;
struct qcom_icc_node **qnodes;
struct qcom_icc_provider *qp;
struct icc_node *node, *tmp;
struct icc_node *node;
size_t num_nodes, i;
int ret;
@ -456,7 +447,7 @@ static int qnoc_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&provider->nodes);
provider->dev = dev;
provider->set = qcom_icc_set;
provider->aggregate = qcom_icc_aggregate;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
provider->data = data;
@ -494,10 +485,7 @@ static int qnoc_probe(struct platform_device *pdev)
return 0;
err:
list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) {
icc_node_del(node);
icc_node_destroy(node->id);
}
icc_nodes_remove(provider);
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
icc_provider_del(provider);
@ -507,16 +495,10 @@ static int qnoc_probe(struct platform_device *pdev)
static int qnoc_remove(struct platform_device *pdev)
{
struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
struct icc_provider *provider = &qp->provider;
struct icc_node *n, *tmp;
list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) {
icc_node_del(n);
icc_node_destroy(n->id);
}
icc_nodes_remove(&qp->provider);
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
return icc_provider_del(provider);
return icc_provider_del(&qp->provider);
}
static const struct of_device_id qcs404_noc_of_match[] = {

View File

@ -855,11 +855,7 @@ static int qnoc_probe(struct platform_device *pdev)
return ret;
err:
list_for_each_entry(node, &provider->nodes, node_list) {
icc_node_del(node);
icc_node_destroy(node->id);
}
icc_nodes_remove(provider);
icc_provider_del(provider);
return ret;
}
@ -867,15 +863,9 @@ static int qnoc_probe(struct platform_device *pdev)
static int qnoc_remove(struct platform_device *pdev)
{
struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
struct icc_provider *provider = &qp->provider;
struct icc_node *n, *tmp;
list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) {
icc_node_del(n);
icc_node_destroy(n->id);
}
return icc_provider_del(provider);
icc_nodes_remove(&qp->provider);
return icc_provider_del(&qp->provider);
}
static const struct of_device_id qnoc_of_match[] = {

View File

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework tracepoints
* Copyright (c) 2019, Linaro Ltd.
* Author: Georgi Djakov <georgi.djakov@linaro.org>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM interconnect
#if !defined(_TRACE_INTERCONNECT_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_INTERCONNECT_H
#include <linux/interconnect.h>
#include <linux/tracepoint.h>
TRACE_EVENT(icc_set_bw,
TP_PROTO(struct icc_path *p, struct icc_node *n, int i,
u32 avg_bw, u32 peak_bw),
TP_ARGS(p, n, i, avg_bw, peak_bw),
TP_STRUCT__entry(
__string(path_name, p->name)
__string(dev, dev_name(p->reqs[i].dev))
__string(node_name, n->name)
__field(u32, avg_bw)
__field(u32, peak_bw)
__field(u32, node_avg_bw)
__field(u32, node_peak_bw)
),
TP_fast_assign(
__assign_str(path_name, p->name);
__assign_str(dev, dev_name(p->reqs[i].dev));
__assign_str(node_name, n->name);
__entry->avg_bw = avg_bw;
__entry->peak_bw = peak_bw;
__entry->node_avg_bw = n->avg_bw;
__entry->node_peak_bw = n->peak_bw;
),
TP_printk("path=%s dev=%s node=%s avg_bw=%u peak_bw=%u agg_avg=%u agg_peak=%u",
__get_str(path_name),
__get_str(dev),
__get_str(node_name),
__entry->avg_bw,
__entry->peak_bw,
__entry->node_avg_bw,
__entry->node_peak_bw)
);
TRACE_EVENT(icc_set_bw_end,
TP_PROTO(struct icc_path *p, int ret),
TP_ARGS(p, ret),
TP_STRUCT__entry(
__string(path_name, p->name)
__string(dev, dev_name(p->reqs[0].dev))
__field(int, ret)
),
TP_fast_assign(
__assign_str(path_name, p->name);
__assign_str(dev, dev_name(p->reqs[0].dev));
__entry->ret = ret;
),
TP_printk("path=%s dev=%s ret=%d",
__get_str(path_name),
__get_str(dev),
__entry->ret)
);
#endif /* _TRACE_INTERCONNECT_H */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>

View File

@ -38,12 +38,18 @@ static const struct alcor_dev_cfg au6621_cfg = {
.dma = 1,
};
static const struct alcor_dev_cfg au6625_cfg = {
.dma = 0,
};
static const struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6601),
.driver_data = (kernel_ulong_t)&alcor_cfg },
{ PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6621),
.driver_data = (kernel_ulong_t)&au6621_cfg },
{ },
{ PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6625),
.driver_data = (kernel_ulong_t)&au6625_cfg },
{},
};
MODULE_DEVICE_TABLE(pci, pci_ids);

View File

@ -628,7 +628,8 @@ int rts5261_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
{
int err, clk;
u8 n, clk_divider, mcu_cnt, div;
u16 n;
u8 clk_divider, mcu_cnt, div;
static const u8 depth[] = {
[RTSX_SSC_DEPTH_4M] = RTS5261_SSC_DEPTH_4M,
[RTSX_SSC_DEPTH_2M] = RTS5261_SSC_DEPTH_2M,
@ -661,13 +662,13 @@ int rts5261_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
return 0;
if (pcr->ops->conv_clk_and_div_n)
n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
n = pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
else
n = (u8)(clk - 4);
n = clk - 4;
if ((clk <= 4) || (n > 396))
return -EINVAL;
mcu_cnt = (u8)(125/clk + 3);
mcu_cnt = 125/clk + 3;
if (mcu_cnt > 15)
mcu_cnt = 15;
@ -676,7 +677,7 @@ int rts5261_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
if (pcr->ops->conv_clk_and_div_n) {
int dbl_clk = pcr->ops->conv_clk_and_div_n(n,
DIV_N_TO_CLK) * 2;
n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
n = pcr->ops->conv_clk_and_div_n(dbl_clk,
CLK_TO_DIV_N);
} else {
n = (n + 4) * 2 - 4;

View File

@ -352,7 +352,7 @@ void cxl_context_free(struct cxl_context *ctx)
void cxl_context_mm_count_get(struct cxl_context *ctx)
{
if (ctx->mm)
atomic_inc(&ctx->mm->mm_count);
mmgrab(ctx->mm);
}
void cxl_context_mm_count_put(struct cxl_context *ctx)

View File

@ -1084,7 +1084,7 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue)
queue->ddcb_daddr);
queue->ddcb_vaddr = NULL;
queue->ddcb_daddr = 0ull;
return -ENODEV;
return rc;
}
@ -1179,7 +1179,7 @@ static irqreturn_t genwqe_vf_isr(int irq, void *dev_id)
*/
static int genwqe_card_thread(void *data)
{
int should_stop = 0, rc = 0;
int should_stop = 0;
struct genwqe_dev *cd = (struct genwqe_dev *)data;
while (!kthread_should_stop()) {
@ -1187,12 +1187,12 @@ static int genwqe_card_thread(void *data)
genwqe_check_ddcb_queue(cd, &cd->queue);
if (GENWQE_POLLING_ENABLED) {
rc = wait_event_interruptible_timeout(
wait_event_interruptible_timeout(
cd->queue_waitq,
genwqe_ddcbs_in_flight(cd) ||
(should_stop = kthread_should_stop()), 1);
} else {
rc = wait_event_interruptible_timeout(
wait_event_interruptible_timeout(
cd->queue_waitq,
genwqe_next_ddcb_ready(cd) ||
(should_stop = kthread_should_stop()), HZ);

View File

@ -173,6 +173,7 @@ static int isl29020_probe(struct i2c_client *client,
static int isl29020_remove(struct i2c_client *client)
{
pm_runtime_disable(&client->dev);
sysfs_remove_group(&client->dev.kobj, &m_als_gr);
return 0;
}

View File

@ -765,7 +765,7 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *a,
struct mei_cl_device *cldev = to_mei_cl_device(dev);
const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%pUl", uuid);
return sprintf(buf, "%pUl", uuid);
}
static DEVICE_ATTR_RO(uuid);
@ -775,7 +775,7 @@ static ssize_t version_show(struct device *dev, struct device_attribute *a,
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u8 version = mei_me_cl_ver(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%02X", version);
return sprintf(buf, "%02X", version);
}
static DEVICE_ATTR_RO(version);
@ -797,7 +797,7 @@ static ssize_t max_conn_show(struct device *dev, struct device_attribute *a,
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u8 maxconn = mei_me_cl_max_conn(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%d", maxconn);
return sprintf(buf, "%d", maxconn);
}
static DEVICE_ATTR_RO(max_conn);
@ -807,7 +807,7 @@ static ssize_t fixed_show(struct device *dev, struct device_attribute *a,
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u8 fixed = mei_me_cl_fixed(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%d", fixed);
return sprintf(buf, "%d", fixed);
}
static DEVICE_ATTR_RO(fixed);
@ -817,7 +817,7 @@ static ssize_t max_len_show(struct device *dev, struct device_attribute *a,
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u32 maxlen = mei_me_cl_max_len(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%u", maxlen);
return sprintf(buf, "%u", maxlen);
}
static DEVICE_ATTR_RO(max_len);

View File

@ -757,11 +757,38 @@ static const struct component_master_ops mei_component_master_ops = {
.unbind = mei_component_master_unbind,
};
/**
* mei_hdcp_component_match - compare function for matching mei hdcp.
*
* The function checks if the driver is i915, the subcomponent is HDCP
* and the grand parent of hdcp and the parent of i915 are the same
* PCH device.
*
* @dev: master device
* @subcomponent: subcomponent to match (I915_COMPONENT_HDCP)
* @data: compare data (mei hdcp device)
*
* Return:
* * 1 - if components match
* * 0 - otherwise
*/
static int mei_hdcp_component_match(struct device *dev, int subcomponent,
void *data)
{
return !strcmp(dev->driver->name, "i915") &&
subcomponent == I915_COMPONENT_HDCP;
struct device *base = data;
if (strcmp(dev->driver->name, "i915") ||
subcomponent != I915_COMPONENT_HDCP)
return 0;
base = base->parent;
if (!base)
return 0;
base = base->parent;
dev = dev->parent;
return (base && dev && dev == base);
}
static int mei_hdcp_probe(struct mei_cl_device *cldev,
@ -785,7 +812,7 @@ static int mei_hdcp_probe(struct mei_cl_device *cldev,
master_match = NULL;
component_match_add_typed(&cldev->dev, &master_match,
mei_hdcp_component_match, comp_master);
mei_hdcp_component_match, &cldev->dev);
if (IS_ERR_OR_NULL(master_match)) {
ret = -ENOMEM;
goto err_exit;

View File

@ -81,10 +81,16 @@
#define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */
#define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */
#define MEI_DEV_ID_CMP_V 0xA3BA /* Comet Point Lake V */
#define MEI_DEV_ID_CMP_H 0x06e0 /* Comet Lake H */
#define MEI_DEV_ID_CMP_H_3 0x06e4 /* Comet Lake H 3 (iTouch) */
#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */
#define MEI_DEV_ID_JSP_N 0x4DE0 /* Jasper Lake Point N */
#define MEI_DEV_ID_TGP_LP 0xA0E0 /* Tiger Lake Point LP */
#define MEI_DEV_ID_MCC 0x4B70 /* Mule Creek Canyon (EHL) */

View File

@ -99,11 +99,15 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_JSP_N, MEI_ME_PCH15_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_MCC, MEI_ME_PCH15_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_MCC_4, MEI_ME_PCH8_CFG)},

View File

@ -65,9 +65,6 @@ void __init mic_create_card_debug_dir(struct mic_driver *mdrv)
*/
void mic_delete_card_debug_dir(struct mic_driver *mdrv)
{
if (!mdrv->dbg_dir)
return;
debugfs_remove_recursive(mdrv->dbg_dir);
}

View File

@ -102,9 +102,6 @@ void cosm_create_debug_dir(struct cosm_device *cdev)
void cosm_delete_debug_dir(struct cosm_device *cdev)
{
if (!cdev->dbg_dir)
return;
debugfs_remove_recursive(cdev->dbg_dir);
}

View File

@ -129,9 +129,6 @@ void mic_create_debug_dir(struct mic_device *mdev)
*/
void mic_delete_debug_dir(struct mic_device *mdev)
{
if (!mdev->dbg_dir)
return;
debugfs_remove_recursive(mdev->dbg_dir);
}

View File

@ -792,7 +792,7 @@ static int pti_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
unsigned int a;
int retval = -EINVAL;
int retval;
int pci_bar = 1;
dev_dbg(&pdev->dev, "%s %s(%d): PTI PCI ID %04x:%04x\n", __FILE__,
@ -910,7 +910,7 @@ static struct pci_driver pti_pci_driver = {
*/
static int __init pti_init(void)
{
int retval = -EINVAL;
int retval;
/* First register module as tty device */

View File

@ -10,16 +10,16 @@
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <uapi/misc/pvpanic.h>
static void __iomem *base;
#define PVPANIC_PANICKED (1 << 0)
MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
MODULE_DESCRIPTION("pvpanic device driver");
MODULE_LICENSE("GPL");
@ -34,7 +34,13 @@ static int
pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
void *unused)
{
pvpanic_send_event(PVPANIC_PANICKED);
unsigned int event = PVPANIC_PANICKED;
if (kexec_crash_loaded())
event = PVPANIC_CRASH_LOADED;
pvpanic_send_event(event);
return NOTIFY_DONE;
}

View File

@ -85,6 +85,7 @@ void *sram_exec_copy(struct gen_pool *pool, void *dst, void *src,
unsigned long base;
int pages;
void *dst_cpy;
int ret;
mutex_lock(&exec_pool_list_mutex);
list_for_each_entry(p, &exec_pool_list, list) {
@ -104,16 +105,28 @@ void *sram_exec_copy(struct gen_pool *pool, void *dst, void *src,
mutex_lock(&part->lock);
set_memory_nx((unsigned long)base, pages);
set_memory_rw((unsigned long)base, pages);
ret = set_memory_nx((unsigned long)base, pages);
if (ret)
goto error_out;
ret = set_memory_rw((unsigned long)base, pages);
if (ret)
goto error_out;
dst_cpy = fncpy(dst, src, size);
set_memory_ro((unsigned long)base, pages);
set_memory_x((unsigned long)base, pages);
ret = set_memory_ro((unsigned long)base, pages);
if (ret)
goto error_out;
ret = set_memory_x((unsigned long)base, pages);
if (ret)
goto error_out;
mutex_unlock(&part->lock);
return dst_cpy;
error_out:
mutex_unlock(&part->lock);
return NULL;
}
EXPORT_SYMBOL_GPL(sram_exec_copy);

View File

@ -736,8 +736,8 @@ static int st_tty_open(struct tty_struct *tty)
static void st_tty_close(struct tty_struct *tty)
{
unsigned char i = ST_MAX_CHANNELS;
unsigned long flags = 0;
unsigned char i;
unsigned long flags;
struct st_data_s *st_gdata = tty->disc_data;
pr_info("%s ", __func__);

View File

@ -148,16 +148,14 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
u16 c0 = count_lut[ch0];
u16 c1 = count_lut[ch1];
/*
* Calculate ratio.
* Note: the "128" is a scaling factor
*/
u8 r = 128;
/* Avoid division by 0 and count 1 cannot be greater than count 0 */
if (c1 <= c0)
if (c0) {
r = c1 * 128 / c0;
/*
* Calculate ratio.
* Note: the "128" is a scaling factor
*/
u8 r = c1 * 128 / c0;
/* Calculate LUX */
lux = ((c0 - c1) * ratio_lut[r]) / 256;

View File

@ -733,7 +733,7 @@ static int xsdfec_set_order(struct xsdfec_dev *xsdfec, void __user *arg)
enum xsdfec_order order;
int err;
err = get_user(order, (enum xsdfec_order *)arg);
err = get_user(order, (enum xsdfec_order __user *)arg);
if (err)
return -EFAULT;
@ -1025,25 +1025,25 @@ static long xsdfec_dev_compat_ioctl(struct file *file, unsigned int cmd,
}
#endif
static unsigned int xsdfec_poll(struct file *file, poll_table *wait)
static __poll_t xsdfec_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
__poll_t mask = 0;
struct xsdfec_dev *xsdfec;
xsdfec = container_of(file->private_data, struct xsdfec_dev, miscdev);
if (!xsdfec)
return POLLNVAL | POLLHUP;
return EPOLLNVAL | EPOLLHUP;
poll_wait(file, &xsdfec->waitq, wait);
/* XSDFEC ISR detected an error */
spin_lock_irqsave(&xsdfec->error_data_lock, xsdfec->flags);
if (xsdfec->state_updated)
mask |= POLLIN | POLLPRI;
mask |= EPOLLIN | EPOLLPRI;
if (xsdfec->stats_updated)
mask |= POLLIN | POLLRDNORM;
mask |= EPOLLIN | EPOLLRDNORM;
spin_unlock_irqrestore(&xsdfec->error_data_lock, xsdfec->flags);
return mask;

View File

@ -109,6 +109,14 @@ config QCOM_QFPROM
This driver can also be built as a module. If so, the module
will be called nvmem_qfprom.
config NVMEM_SPMI_SDAM
tristate "SPMI SDAM Support"
depends on SPMI
help
This driver supports the Shared Direct Access Memory Module on
Qualcomm Technologies, Inc. PMICs. It provides the clients
an interface to read/write to the SDAM module's shared memory.
config ROCKCHIP_EFUSE
tristate "Rockchip eFuse Support"
depends on ARCH_ROCKCHIP || COMPILE_TEST

View File

@ -28,6 +28,8 @@ obj-$(CONFIG_MTK_EFUSE) += nvmem_mtk-efuse.o
nvmem_mtk-efuse-y := mtk-efuse.o
obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o
nvmem_qfprom-y := qfprom.o
obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o
nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o
obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
nvmem_rockchip_efuse-y := rockchip-efuse.o
obj-$(CONFIG_ROCKCHIP_OTP) += nvmem-rockchip-otp.o

View File

@ -83,7 +83,7 @@ static void nvmem_cell_drop(struct nvmem_cell *cell)
list_del(&cell->node);
mutex_unlock(&nvmem_mutex);
of_node_put(cell->np);
kfree(cell->name);
kfree_const(cell->name);
kfree(cell);
}
@ -110,7 +110,9 @@ static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem,
cell->nvmem = nvmem;
cell->offset = info->offset;
cell->bytes = info->bytes;
cell->name = info->name;
cell->name = kstrdup_const(info->name, GFP_KERNEL);
if (!cell->name)
return -ENOMEM;
cell->bit_offset = info->bit_offset;
cell->nbits = info->nbits;
@ -300,7 +302,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
dev_err(dev, "cell %s unaligned to nvmem stride %d\n",
cell->name, nvmem->stride);
/* Cells already added will be freed later. */
kfree(cell->name);
kfree_const(cell->name);
kfree(cell);
return -EINVAL;
}

View File

@ -15,8 +15,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#define IMX_SIP_OTP 0xC200000A
#define IMX_SIP_OTP_WRITE 0x2
#define IMX_SIP_OTP_WRITE 0xc200000B
enum ocotp_devtype {
IMX8QXP,
@ -139,8 +138,8 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset,
void *p;
int i, ret;
index = offset >> 2;
num_bytes = round_up((offset % 4) + bytes, 4);
index = offset;
num_bytes = round_up(bytes, 4);
count = num_bytes >> 2;
if (count > (priv->data->nregs - index))
@ -169,7 +168,7 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset,
buf++;
}
memcpy(val, (u8 *)p + offset % 4, bytes);
memcpy(val, (u8 *)p, bytes);
mutex_unlock(&scu_ocotp_mutex);
@ -189,10 +188,10 @@ static int imx_scu_ocotp_write(void *context, unsigned int offset,
int ret;
/* allow only writing one complete OTP word at a time */
if ((bytes != 4) || (offset % 4))
if (bytes != 4)
return -EINVAL;
index = offset >> 2;
index = offset;
if (in_hole(context, index))
return -EINVAL;
@ -212,8 +211,7 @@ static int imx_scu_ocotp_write(void *context, unsigned int offset,
mutex_lock(&scu_ocotp_mutex);
arm_smccc_smc(IMX_SIP_OTP, IMX_SIP_OTP_WRITE, index, *buf,
0, 0, 0, 0, &res);
arm_smccc_smc(IMX_SIP_OTP_WRITE, index, *buf, 0, 0, 0, 0, 0, &res);
mutex_unlock(&scu_ocotp_mutex);

View File

@ -44,6 +44,14 @@
#define IMX_OCOTP_BM_CTRL_ERROR 0x00000200
#define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400
#define IMX_OCOTP_BM_CTRL_DEFAULT \
{ \
.bm_addr = IMX_OCOTP_BM_CTRL_ADDR, \
.bm_busy = IMX_OCOTP_BM_CTRL_BUSY, \
.bm_error = IMX_OCOTP_BM_CTRL_ERROR, \
.bm_rel_shadows = IMX_OCOTP_BM_CTRL_REL_SHADOWS,\
}
#define TIMING_STROBE_PROG_US 10 /* Min time to blow a fuse */
#define TIMING_STROBE_READ_NS 37 /* Min time before read */
#define TIMING_RELAX_NS 17
@ -62,18 +70,31 @@ struct ocotp_priv {
struct nvmem_config *config;
};
struct ocotp_ctrl_reg {
u32 bm_addr;
u32 bm_busy;
u32 bm_error;
u32 bm_rel_shadows;
};
struct ocotp_params {
unsigned int nregs;
unsigned int bank_address_words;
void (*set_timing)(struct ocotp_priv *priv);
struct ocotp_ctrl_reg ctrl;
};
static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags)
static int imx_ocotp_wait_for_busy(struct ocotp_priv *priv, u32 flags)
{
int count;
u32 c, mask;
u32 bm_ctrl_busy, bm_ctrl_error;
void __iomem *base = priv->base;
mask = IMX_OCOTP_BM_CTRL_BUSY | IMX_OCOTP_BM_CTRL_ERROR | flags;
bm_ctrl_busy = priv->params->ctrl.bm_busy;
bm_ctrl_error = priv->params->ctrl.bm_error;
mask = bm_ctrl_busy | bm_ctrl_error | flags;
for (count = 10000; count >= 0; count--) {
c = readl(base + IMX_OCOTP_ADDR_CTRL);
@ -97,7 +118,7 @@ static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags)
* - A read is performed to from a fuse word which has been read
* locked.
*/
if (c & IMX_OCOTP_BM_CTRL_ERROR)
if (c & bm_ctrl_error)
return -EPERM;
return -ETIMEDOUT;
}
@ -105,15 +126,18 @@ static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags)
return 0;
}
static void imx_ocotp_clr_err_if_set(void __iomem *base)
static void imx_ocotp_clr_err_if_set(struct ocotp_priv *priv)
{
u32 c;
u32 c, bm_ctrl_error;
void __iomem *base = priv->base;
bm_ctrl_error = priv->params->ctrl.bm_error;
c = readl(base + IMX_OCOTP_ADDR_CTRL);
if (!(c & IMX_OCOTP_BM_CTRL_ERROR))
if (!(c & bm_ctrl_error))
return;
writel(IMX_OCOTP_BM_CTRL_ERROR, base + IMX_OCOTP_ADDR_CTRL_CLR);
writel(bm_ctrl_error, base + IMX_OCOTP_ADDR_CTRL_CLR);
}
static int imx_ocotp_read(void *context, unsigned int offset,
@ -140,7 +164,7 @@ static int imx_ocotp_read(void *context, unsigned int offset,
return ret;
}
ret = imx_ocotp_wait_for_busy(priv->base, 0);
ret = imx_ocotp_wait_for_busy(priv, 0);
if (ret < 0) {
dev_err(priv->dev, "timeout during read setup\n");
goto read_end;
@ -157,7 +181,7 @@ static int imx_ocotp_read(void *context, unsigned int offset,
* issued
*/
if (*(buf - 1) == IMX_OCOTP_READ_LOCKED_VAL)
imx_ocotp_clr_err_if_set(priv->base);
imx_ocotp_clr_err_if_set(priv);
}
ret = 0;
@ -274,7 +298,7 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val,
* write or reload must be completed before a write access can be
* requested.
*/
ret = imx_ocotp_wait_for_busy(priv->base, 0);
ret = imx_ocotp_wait_for_busy(priv, 0);
if (ret < 0) {
dev_err(priv->dev, "timeout during timing setup\n");
goto write_end;
@ -306,8 +330,8 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val,
}
ctrl = readl(priv->base + IMX_OCOTP_ADDR_CTRL);
ctrl &= ~IMX_OCOTP_BM_CTRL_ADDR;
ctrl |= waddr & IMX_OCOTP_BM_CTRL_ADDR;
ctrl &= ~priv->params->ctrl.bm_addr;
ctrl |= waddr & priv->params->ctrl.bm_addr;
ctrl |= IMX_OCOTP_WR_UNLOCK;
writel(ctrl, priv->base + IMX_OCOTP_ADDR_CTRL);
@ -374,11 +398,11 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val,
* be set. It must be cleared by software before any new write access
* can be issued.
*/
ret = imx_ocotp_wait_for_busy(priv->base, 0);
ret = imx_ocotp_wait_for_busy(priv, 0);
if (ret < 0) {
if (ret == -EPERM) {
dev_err(priv->dev, "failed write to locked region");
imx_ocotp_clr_err_if_set(priv->base);
imx_ocotp_clr_err_if_set(priv);
} else {
dev_err(priv->dev, "timeout during data write\n");
}
@ -394,10 +418,10 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val,
udelay(2);
/* reload all shadow registers */
writel(IMX_OCOTP_BM_CTRL_REL_SHADOWS,
writel(priv->params->ctrl.bm_rel_shadows,
priv->base + IMX_OCOTP_ADDR_CTRL_SET);
ret = imx_ocotp_wait_for_busy(priv->base,
IMX_OCOTP_BM_CTRL_REL_SHADOWS);
ret = imx_ocotp_wait_for_busy(priv,
priv->params->ctrl.bm_rel_shadows);
if (ret < 0) {
dev_err(priv->dev, "timeout during shadow register reload\n");
goto write_end;
@ -424,65 +448,76 @@ static const struct ocotp_params imx6q_params = {
.nregs = 128,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx6sl_params = {
.nregs = 64,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx6sll_params = {
.nregs = 128,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx6sx_params = {
.nregs = 128,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx6ul_params = {
.nregs = 128,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx6ull_params = {
.nregs = 64,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx7d_params = {
.nregs = 64,
.bank_address_words = 4,
.set_timing = imx_ocotp_set_imx7_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx7ulp_params = {
.nregs = 256,
.bank_address_words = 0,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx8mq_params = {
.nregs = 256,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx8mm_params = {
.nregs = 256,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct ocotp_params imx8mn_params = {
.nregs = 256,
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
};
static const struct of_device_id imx_ocotp_dt_ids[] = {
@ -521,17 +556,17 @@ static int imx_ocotp_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
clk_prepare_enable(priv->clk);
imx_ocotp_clr_err_if_set(priv->base);
clk_disable_unprepare(priv->clk);
priv->params = of_device_get_match_data(&pdev->dev);
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
priv->config = &imx_ocotp_nvmem_config;
nvmem = devm_nvmem_register(dev, &imx_ocotp_nvmem_config);
clk_prepare_enable(priv->clk);
imx_ocotp_clr_err_if_set(priv);
clk_disable_unprepare(priv->clk);
nvmem = devm_nvmem_register(dev, &imx_ocotp_nvmem_config);
return PTR_ERR_OR_ZERO(nvmem);
}

View File

@ -0,0 +1,192 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 The Linux Foundation. All rights reserved.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#define SDAM_MEM_START 0x40
#define REGISTER_MAP_ID 0x40
#define REGISTER_MAP_VERSION 0x41
#define SDAM_SIZE 0x44
#define SDAM_PBS_TRIG_SET 0xE5
#define SDAM_PBS_TRIG_CLR 0xE6
struct sdam_chip {
struct platform_device *pdev;
struct regmap *regmap;
struct nvmem_config sdam_config;
unsigned int base;
unsigned int size;
};
/* read only register offsets */
static const u8 sdam_ro_map[] = {
REGISTER_MAP_ID,
REGISTER_MAP_VERSION,
SDAM_SIZE
};
static bool sdam_is_valid(struct sdam_chip *sdam, unsigned int offset,
size_t len)
{
unsigned int sdam_mem_end = SDAM_MEM_START + sdam->size - 1;
if (!len)
return false;
if (offset >= SDAM_MEM_START && offset <= sdam_mem_end
&& (offset + len - 1) <= sdam_mem_end)
return true;
else if ((offset == SDAM_PBS_TRIG_SET || offset == SDAM_PBS_TRIG_CLR)
&& (len == 1))
return true;
return false;
}
static bool sdam_is_ro(unsigned int offset, size_t len)
{
int i;
for (i = 0; i < ARRAY_SIZE(sdam_ro_map); i++)
if (offset <= sdam_ro_map[i] && (offset + len) > sdam_ro_map[i])
return true;
return false;
}
static int sdam_read(void *priv, unsigned int offset, void *val,
size_t bytes)
{
struct sdam_chip *sdam = priv;
struct device *dev = &sdam->pdev->dev;
int rc;
if (!sdam_is_valid(sdam, offset, bytes)) {
dev_err(dev, "Invalid SDAM offset %#x len=%zd\n",
offset, bytes);
return -EINVAL;
}
rc = regmap_bulk_read(sdam->regmap, sdam->base + offset, val, bytes);
if (rc < 0)
dev_err(dev, "Failed to read SDAM offset %#x len=%zd, rc=%d\n",
offset, bytes, rc);
return rc;
}
static int sdam_write(void *priv, unsigned int offset, void *val,
size_t bytes)
{
struct sdam_chip *sdam = priv;
struct device *dev = &sdam->pdev->dev;
int rc;
if (!sdam_is_valid(sdam, offset, bytes)) {
dev_err(dev, "Invalid SDAM offset %#x len=%zd\n",
offset, bytes);
return -EINVAL;
}
if (sdam_is_ro(offset, bytes)) {
dev_err(dev, "Invalid write offset %#x len=%zd\n",
offset, bytes);
return -EINVAL;
}
rc = regmap_bulk_write(sdam->regmap, sdam->base + offset, val, bytes);
if (rc < 0)
dev_err(dev, "Failed to write SDAM offset %#x len=%zd, rc=%d\n",
offset, bytes, rc);
return rc;
}
static int sdam_probe(struct platform_device *pdev)
{
struct sdam_chip *sdam;
struct nvmem_device *nvmem;
unsigned int val;
int rc;
sdam = devm_kzalloc(&pdev->dev, sizeof(*sdam), GFP_KERNEL);
if (!sdam)
return -ENOMEM;
sdam->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!sdam->regmap) {
dev_err(&pdev->dev, "Failed to get regmap handle\n");
return -ENXIO;
}
rc = of_property_read_u32(pdev->dev.of_node, "reg", &sdam->base);
if (rc < 0) {
dev_err(&pdev->dev, "Failed to get SDAM base, rc=%d\n", rc);
return -EINVAL;
}
rc = regmap_read(sdam->regmap, sdam->base + SDAM_SIZE, &val);
if (rc < 0) {
dev_err(&pdev->dev, "Failed to read SDAM_SIZE rc=%d\n", rc);
return -EINVAL;
}
sdam->size = val * 32;
sdam->sdam_config.dev = &pdev->dev;
sdam->sdam_config.name = "spmi_sdam";
sdam->sdam_config.id = pdev->id;
sdam->sdam_config.owner = THIS_MODULE,
sdam->sdam_config.stride = 1;
sdam->sdam_config.word_size = 1;
sdam->sdam_config.reg_read = sdam_read;
sdam->sdam_config.reg_write = sdam_write;
sdam->sdam_config.priv = sdam;
nvmem = devm_nvmem_register(&pdev->dev, &sdam->sdam_config);
if (IS_ERR(nvmem)) {
dev_err(&pdev->dev,
"Failed to register SDAM nvmem device rc=%ld\n",
PTR_ERR(nvmem));
return -ENXIO;
}
dev_dbg(&pdev->dev,
"SDAM base=%#x size=%u registered successfully\n",
sdam->base, sdam->size);
return 0;
}
static const struct of_device_id sdam_match_table[] = {
{ .compatible = "qcom,spmi-sdam" },
{},
};
static struct platform_driver sdam_driver = {
.driver = {
.name = "qcom,spmi-sdam",
.of_match_table = sdam_match_table,
},
.probe = sdam_probe,
};
static int __init sdam_init(void)
{
return platform_driver_register(&sdam_driver);
}
subsys_initcall(sdam_init);
static void __exit sdam_exit(void)
{
return platform_driver_unregister(&sdam_driver);
}
module_exit(sdam_exit);
MODULE_DESCRIPTION("QCOM SPMI SDAM driver");
MODULE_LICENSE("GPL v2");

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2015-2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
*/

View File

@ -641,6 +641,8 @@ static int qcom_slim_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
slim_unregister_controller(&ctrl->ctrl);
clk_disable_unprepare(ctrl->rclk);
clk_disable_unprepare(ctrl->hclk);
destroy_workqueue(ctrl->rxwq);
return 0;
}

View File

@ -666,10 +666,12 @@ static int qcom_slim_ngd_init_rx_msgq(struct qcom_slim_ngd_ctrl *ctrl)
struct device *dev = ctrl->dev;
int ret, size;
ctrl->dma_rx_channel = dma_request_slave_channel(dev, "rx");
if (!ctrl->dma_rx_channel) {
dev_err(dev, "Failed to request dma channels");
return -EINVAL;
ctrl->dma_rx_channel = dma_request_chan(dev, "rx");
if (IS_ERR(ctrl->dma_rx_channel)) {
dev_err(dev, "Failed to request RX dma channel");
ret = PTR_ERR(ctrl->dma_rx_channel);
ctrl->dma_rx_channel = NULL;
return ret;
}
size = QCOM_SLIM_NGD_DESC_NUM * SLIM_MSGQ_BUF_LEN;
@ -703,10 +705,12 @@ static int qcom_slim_ngd_init_tx_msgq(struct qcom_slim_ngd_ctrl *ctrl)
int ret = 0;
int size;
ctrl->dma_tx_channel = dma_request_slave_channel(dev, "tx");
if (!ctrl->dma_tx_channel) {
dev_err(dev, "Failed to request dma channels");
return -EINVAL;
ctrl->dma_tx_channel = dma_request_chan(dev, "tx");
if (IS_ERR(ctrl->dma_tx_channel)) {
dev_err(dev, "Failed to request TX dma channel");
ret = PTR_ERR(ctrl->dma_tx_channel);
ctrl->dma_tx_channel = NULL;
return ret;
}
size = ((QCOM_SLIM_NGD_DESC_NUM + 1) * SLIM_MSGQ_BUF_LEN);

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2011-2017, The Linux Foundation
*/

View File

@ -31,4 +31,13 @@ config SOUNDWIRE_INTEL
enable this config option to get the SoundWire support for that
device.
config SOUNDWIRE_QCOM
tristate "Qualcomm SoundWire Master driver"
depends on SLIMBUS
depends on SND_SOC
help
SoundWire Qualcomm Master driver.
If you have an Qualcomm platform which has a SoundWire Master then
enable this config option to get the SoundWire support for that
device
endif

View File

@ -21,3 +21,7 @@ obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o
soundwire-intel-init-objs := intel_init.o
obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel-init.o
#Qualcomm driver
soundwire-qcom-objs := qcom.o
obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o

View File

@ -456,26 +456,35 @@ static int sdw_get_device_num(struct sdw_slave *slave)
static int sdw_assign_device_num(struct sdw_slave *slave)
{
int ret, dev_num;
bool new_device = false;
/* check first if device number is assigned, if so reuse that */
if (!slave->dev_num) {
mutex_lock(&slave->bus->bus_lock);
dev_num = sdw_get_device_num(slave);
mutex_unlock(&slave->bus->bus_lock);
if (dev_num < 0) {
dev_err(slave->bus->dev, "Get dev_num failed: %d\n",
dev_num);
return dev_num;
if (!slave->dev_num_sticky) {
mutex_lock(&slave->bus->bus_lock);
dev_num = sdw_get_device_num(slave);
mutex_unlock(&slave->bus->bus_lock);
if (dev_num < 0) {
dev_err(slave->bus->dev, "Get dev_num failed: %d\n",
dev_num);
return dev_num;
}
slave->dev_num = dev_num;
slave->dev_num_sticky = dev_num;
new_device = true;
} else {
slave->dev_num = slave->dev_num_sticky;
}
} else {
}
if (!new_device)
dev_info(slave->bus->dev,
"Slave already registered dev_num:%d\n",
"Slave already registered, reusing dev_num:%d\n",
slave->dev_num);
/* Clear the slave->dev_num to transfer message on device 0 */
dev_num = slave->dev_num;
slave->dev_num = 0;
}
/* Clear the slave->dev_num to transfer message on device 0 */
dev_num = slave->dev_num;
slave->dev_num = 0;
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num);
if (ret < 0) {
@ -485,7 +494,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
}
/* After xfer of msg, restore dev_num */
slave->dev_num = dev_num;
slave->dev_num = slave->dev_num_sticky;
return 0;
}
@ -979,6 +988,24 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
struct sdw_slave *slave;
int i, ret = 0;
/* first check if any Slaves fell off the bus */
for (i = 1; i <= SDW_MAX_DEVICES; i++) {
mutex_lock(&bus->bus_lock);
if (test_bit(i, bus->assigned) == false) {
mutex_unlock(&bus->bus_lock);
continue;
}
mutex_unlock(&bus->bus_lock);
slave = sdw_get_slave(bus, i);
if (!slave)
continue;
if (status[i] == SDW_SLAVE_UNATTACHED &&
slave->status != SDW_SLAVE_UNATTACHED)
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
}
if (status[0] == SDW_SLAVE_ATTACHED) {
dev_dbg(bus->dev, "Slave attached, programming device number\n");
ret = sdw_program_device_num(bus);

View File

@ -74,6 +74,7 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_MCP_INTMASK 0x48
#define CDNS_MCP_INT_IRQ BIT(31)
#define CDNS_MCP_INT_RESERVED1 GENMASK(30, 17)
#define CDNS_MCP_INT_WAKEUP BIT(16)
#define CDNS_MCP_INT_SLAVE_RSVD BIT(15)
#define CDNS_MCP_INT_SLAVE_ALERT BIT(14)
@ -85,10 +86,12 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_MCP_INT_DATA_CLASH BIT(9)
#define CDNS_MCP_INT_PARITY BIT(8)
#define CDNS_MCP_INT_CMD_ERR BIT(7)
#define CDNS_MCP_INT_RESERVED2 GENMASK(6, 4)
#define CDNS_MCP_INT_RX_NE BIT(3)
#define CDNS_MCP_INT_RX_WL BIT(2)
#define CDNS_MCP_INT_TXE BIT(1)
#define CDNS_MCP_INT_TXF BIT(0)
#define CDNS_MCP_INT_RESERVED (CDNS_MCP_INT_RESERVED1 | CDNS_MCP_INT_RESERVED2)
#define CDNS_MCP_INTSET 0x4C
@ -444,7 +447,8 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
time = wait_for_completion_timeout(&cdns->tx_complete,
msecs_to_jiffies(CDNS_TX_TIMEOUT));
if (!time) {
dev_err(cdns->dev, "IO transfer timed out\n");
dev_err(cdns->dev, "IO transfer timed out, cmd %d device %d addr %x len %d\n",
cmd, msg->dev_num, msg->addr, msg->len);
msg->len = 0;
return SDW_CMD_TIMEOUT;
}
@ -672,13 +676,36 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
/* first check if Slave reported multiple status */
if (set_status > 1) {
u32 val;
dev_warn_ratelimited(cdns->dev,
"Slave reported multiple Status: %d\n",
mask);
/*
* TODO: we need to reread the status here by
* issuing a PING cmd
*/
"Slave %d reported multiple Status: %d\n",
i, mask);
/* check latest status extracted from PING commands */
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);
switch (val & 0x3) {
case 0:
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
case 3:
default:
status[i] = SDW_SLAVE_RESERVED;
break;
}
dev_warn_ratelimited(cdns->dev,
"Slave %d status updated to %d\n",
i, status[i]);
}
}
@ -705,6 +732,10 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT);
/* check for reserved values read as zero */
if (int_status & CDNS_MCP_INT_RESERVED)
return IRQ_NONE;
if (!(int_status & CDNS_MCP_INT_IRQ))
return IRQ_NONE;
@ -812,8 +843,9 @@ int sdw_cdns_exit_reset(struct sdw_cdns *cdns)
EXPORT_SYMBOL(sdw_cdns_exit_reset);
/**
* sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
* sdw_cdns_enable_interrupt() - Enable SDW interrupts
* @cdns: Cadence instance
* @state: True if we are trying to enable interrupt.
*/
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
{
@ -849,12 +881,21 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
mask = interrupt_mask;
update_masks:
/* clear slave interrupt status before enabling interrupt */
if (state) {
u32 slave_state;
slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave_state);
slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state);
}
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1);
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
/* commit changes */
return cdns_update_config(cdns);
return 0;
}
EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
@ -948,8 +989,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
ret = cdns_allocate_pdi(cdns, &stream->out,
stream->num_out, offset);
offset += stream->num_out;
if (ret)
return ret;
@ -1224,8 +1263,10 @@ EXPORT_SYMBOL(cdns_set_sdw_stream);
* cdns_find_pdi() - Find a free PDI
*
* @cdns: Cadence instance
* @offset: Starting offset
* @num: Number of PDIs
* @pdi: PDI instances
* @dai_id: DAI id
*
* Find a PDI for a given PDI array. The PDI num and dai_id are
* expected to match, return NULL otherwise.
@ -1277,6 +1318,7 @@ EXPORT_SYMBOL(sdw_cdns_config_stream);
* @stream: Stream to be allocated
* @ch: Channel count
* @dir: Data direction
* @dai_id: DAI id
*/
struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
struct sdw_cdns_streams *stream,

View File

@ -880,6 +880,9 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus)
"intel-sdw-ip-clock",
&prop->mclk_freq);
/* the values reported by BIOS are the 2x clock, not the bus clock */
prop->mclk_freq /= 2;
fwnode_property_read_u32(link,
"intel-quirk-mask",
&quirk_mask);

861
drivers/soundwire/qcom.c Normal file
View File

@ -0,0 +1,861 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019, Linaro Limited
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/slimbus.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "bus.h"
#define SWRM_COMP_HW_VERSION 0x00
#define SWRM_COMP_CFG_ADDR 0x04
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1)
#define SWRM_COMP_CFG_ENABLE_MSK BIT(0)
#define SWRM_COMP_PARAMS 0x100
#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0)
#define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5)
#define SWRM_INTERRUPT_STATUS 0x200
#define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0)
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED BIT(1)
#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS BIT(2)
#define SWRM_INTERRUPT_STATUS_CMD_ERROR BIT(7)
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10)
#define SWRM_INTERRUPT_MASK_ADDR 0x204
#define SWRM_INTERRUPT_CLEAR 0x208
#define SWRM_CMD_FIFO_WR_CMD 0x300
#define SWRM_CMD_FIFO_RD_CMD 0x304
#define SWRM_CMD_FIFO_CMD 0x308
#define SWRM_CMD_FIFO_STATUS 0x30C
#define SWRM_CMD_FIFO_CFG_ADDR 0x314
#define SWRM_RD_WR_CMD_RETRIES 0x7
#define SWRM_CMD_FIFO_RD_FIFO_ADDR 0x318
#define SWRM_ENUMERATOR_CFG_ADDR 0x500
#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (0x101C + 0x40 * (m))
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK GENMASK(2, 0)
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK GENMASK(7, 3)
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0
#define SWRM_MCP_CFG_ADDR 0x1048
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK GENMASK(21, 17)
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11
#define SWRM_DEF_CMD_NO_PINGS 0x1f
#define SWRM_MCP_STATUS 0x104C
#define SWRM_MCP_STATUS_BANK_NUM_MASK BIT(0)
#define SWRM_MCP_SLV_STATUS 0x1090
#define SWRM_MCP_SLV_STATUS_MASK GENMASK(1, 0)
#define SWRM_DP_PORT_CTRL_BANK(n, m) (0x1124 + 0x100 * (n - 1) + 0x40 * m)
#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18
#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10
#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08
#define SWRM_AHB_BRIDGE_WR_DATA_0 0xc85
#define SWRM_AHB_BRIDGE_WR_ADDR_0 0xc89
#define SWRM_AHB_BRIDGE_RD_ADDR_0 0xc8d
#define SWRM_AHB_BRIDGE_RD_DATA_0 0xc91
#define SWRM_REG_VAL_PACK(data, dev, id, reg) \
((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24))
#define SWRM_MAX_ROW_VAL 0 /* Rows = 48 */
#define SWRM_DEFAULT_ROWS 48
#define SWRM_MIN_COL_VAL 0 /* Cols = 2 */
#define SWRM_DEFAULT_COL 16
#define SWRM_MAX_COL_VAL 7
#define SWRM_SPECIAL_CMD_ID 0xF
#define MAX_FREQ_NUM 1
#define TIMEOUT_MS (2 * HZ)
#define QCOM_SWRM_MAX_RD_LEN 0xf
#define QCOM_SDW_MAX_PORTS 14
#define DEFAULT_CLK_FREQ 9600000
#define SWRM_MAX_DAIS 0xF
struct qcom_swrm_port_config {
u8 si;
u8 off1;
u8 off2;
};
struct qcom_swrm_ctrl {
struct sdw_bus bus;
struct device *dev;
struct regmap *regmap;
struct completion *comp;
struct work_struct slave_work;
/* read/write lock */
spinlock_t comp_lock;
/* Port alloc/free lock */
struct mutex port_lock;
struct clk *hclk;
u8 wr_cmd_id;
u8 rd_cmd_id;
int irq;
unsigned int version;
int num_din_ports;
int num_dout_ports;
unsigned long dout_port_mask;
unsigned long din_port_mask;
struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS];
struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS];
enum sdw_slave_status status[SDW_MAX_DEVICES];
int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val);
int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val);
};
#define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus)
static int qcom_swrm_abh_reg_read(struct qcom_swrm_ctrl *ctrl, int reg,
u32 *val)
{
struct regmap *wcd_regmap = ctrl->regmap;
int ret;
/* pg register + offset */
ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_RD_ADDR_0,
(u8 *)&reg, 4);
if (ret < 0)
return SDW_CMD_FAIL;
ret = regmap_bulk_read(wcd_regmap, SWRM_AHB_BRIDGE_RD_DATA_0,
val, 4);
if (ret < 0)
return SDW_CMD_FAIL;
return SDW_CMD_OK;
}
static int qcom_swrm_ahb_reg_write(struct qcom_swrm_ctrl *ctrl,
int reg, int val)
{
struct regmap *wcd_regmap = ctrl->regmap;
int ret;
/* pg register + offset */
ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_DATA_0,
(u8 *)&val, 4);
if (ret)
return SDW_CMD_FAIL;
/* write address register */
ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_ADDR_0,
(u8 *)&reg, 4);
if (ret)
return SDW_CMD_FAIL;
return SDW_CMD_OK;
}
static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data,
u8 dev_addr, u16 reg_addr)
{
DECLARE_COMPLETION_ONSTACK(comp);
unsigned long flags;
u32 val;
int ret;
spin_lock_irqsave(&ctrl->comp_lock, flags);
ctrl->comp = &comp;
spin_unlock_irqrestore(&ctrl->comp_lock, flags);
val = SWRM_REG_VAL_PACK(cmd_data, dev_addr,
SWRM_SPECIAL_CMD_ID, reg_addr);
ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_WR_CMD, val);
if (ret)
goto err;
ret = wait_for_completion_timeout(ctrl->comp,
msecs_to_jiffies(TIMEOUT_MS));
if (!ret)
ret = SDW_CMD_IGNORED;
else
ret = SDW_CMD_OK;
err:
spin_lock_irqsave(&ctrl->comp_lock, flags);
ctrl->comp = NULL;
spin_unlock_irqrestore(&ctrl->comp_lock, flags);
return ret;
}
static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *ctrl,
u8 dev_addr, u16 reg_addr,
u32 len, u8 *rval)
{
int i, ret;
u32 val;
DECLARE_COMPLETION_ONSTACK(comp);
unsigned long flags;
spin_lock_irqsave(&ctrl->comp_lock, flags);
ctrl->comp = &comp;
spin_unlock_irqrestore(&ctrl->comp_lock, flags);
val = SWRM_REG_VAL_PACK(len, dev_addr, SWRM_SPECIAL_CMD_ID, reg_addr);
ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_RD_CMD, val);
if (ret)
goto err;
ret = wait_for_completion_timeout(ctrl->comp,
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
ret = SDW_CMD_IGNORED;
goto err;
} else {
ret = SDW_CMD_OK;
}
for (i = 0; i < len; i++) {
ctrl->reg_read(ctrl, SWRM_CMD_FIFO_RD_FIFO_ADDR, &val);
rval[i] = val & 0xFF;
}
err:
spin_lock_irqsave(&ctrl->comp_lock, flags);
ctrl->comp = NULL;
spin_unlock_irqrestore(&ctrl->comp_lock, flags);
return ret;
}
static void qcom_swrm_get_device_status(struct qcom_swrm_ctrl *ctrl)
{
u32 val;
int i;
ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val);
for (i = 0; i < SDW_MAX_DEVICES; i++) {
u32 s;
s = (val >> (i * 2));
s &= SWRM_MCP_SLV_STATUS_MASK;
ctrl->status[i] = s;
}
}
static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)
{
struct qcom_swrm_ctrl *ctrl = dev_id;
u32 sts, value;
unsigned long flags;
ctrl->reg_read(ctrl, SWRM_INTERRUPT_STATUS, &sts);
if (sts & SWRM_INTERRUPT_STATUS_CMD_ERROR) {
ctrl->reg_read(ctrl, SWRM_CMD_FIFO_STATUS, &value);
dev_err_ratelimited(ctrl->dev,
"CMD error, fifo status 0x%x\n",
value);
ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, 0x1);
}
if ((sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED) ||
sts & SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS)
schedule_work(&ctrl->slave_work);
/**
* clear the interrupt before complete() is called, as complete can
* schedule new read/writes which require interrupts, clearing the
* interrupt would avoid missing interrupts in such cases.
*/
ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR, sts);
if (sts & SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED) {
spin_lock_irqsave(&ctrl->comp_lock, flags);
if (ctrl->comp)
complete(ctrl->comp);
spin_unlock_irqrestore(&ctrl->comp_lock, flags);
}
return IRQ_HANDLED;
}
static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
{
u32 val;
/* Clear Rows and Cols */
val = (SWRM_MAX_ROW_VAL << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT |
SWRM_MIN_COL_VAL << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT);
ctrl->reg_write(ctrl, SWRM_MCP_FRAME_CTRL_BANK_ADDR(0), val);
/* Disable Auto enumeration */
ctrl->reg_write(ctrl, SWRM_ENUMERATOR_CFG_ADDR, 0);
/* Mask soundwire interrupts */
ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR,
SWRM_INTERRUPT_STATUS_RMSK);
/* Configure No pings */
ctrl->reg_read(ctrl, SWRM_MCP_CFG_ADDR, &val);
val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK;
val |= (SWRM_DEF_CMD_NO_PINGS <<
SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT);
ctrl->reg_write(ctrl, SWRM_MCP_CFG_ADDR, val);
/* Configure number of retries of a read/write cmd */
ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CFG_ADDR, SWRM_RD_WR_CMD_RETRIES);
/* Set IRQ to PULSE */
ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR,
SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK |
SWRM_COMP_CFG_ENABLE_MSK);
return 0;
}
static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,
struct sdw_msg *msg)
{
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
int ret, i, len;
if (msg->flags == SDW_MSG_FLAG_READ) {
for (i = 0; i < msg->len;) {
if ((msg->len - i) < QCOM_SWRM_MAX_RD_LEN)
len = msg->len - i;
else
len = QCOM_SWRM_MAX_RD_LEN;
ret = qcom_swrm_cmd_fifo_rd_cmd(ctrl, msg->dev_num,
msg->addr + i, len,
&msg->buf[i]);
if (ret)
return ret;
i = i + len;
}
} else if (msg->flags == SDW_MSG_FLAG_WRITE) {
for (i = 0; i < msg->len; i++) {
ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->buf[i],
msg->dev_num,
msg->addr + i);
if (ret)
return SDW_CMD_IGNORED;
}
}
return SDW_CMD_OK;
}
static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus)
{
u32 reg = SWRM_MCP_FRAME_CTRL_BANK_ADDR(bus->params.next_bank);
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
u32 val;
ctrl->reg_read(ctrl, reg, &val);
val &= ~SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK;
val &= ~SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK;
val |= (SWRM_MAX_ROW_VAL << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT |
SWRM_MAX_COL_VAL << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT);
return ctrl->reg_write(ctrl, reg, val);
}
static int qcom_swrm_port_params(struct sdw_bus *bus,
struct sdw_port_params *p_params,
unsigned int bank)
{
/* TBD */
return 0;
}
static int qcom_swrm_transport_params(struct sdw_bus *bus,
struct sdw_transport_params *params,
enum sdw_reg_bank bank)
{
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
u32 value;
value = params->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT;
value |= params->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT;
value |= params->sample_interval - 1;
return ctrl->reg_write(ctrl,
SWRM_DP_PORT_CTRL_BANK((params->port_num), bank),
value);
}
static int qcom_swrm_port_enable(struct sdw_bus *bus,
struct sdw_enable_ch *enable_ch,
unsigned int bank)
{
u32 reg = SWRM_DP_PORT_CTRL_BANK(enable_ch->port_num, bank);
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
u32 val;
ctrl->reg_read(ctrl, reg, &val);
if (enable_ch->enable)
val |= (enable_ch->ch_mask << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
else
val &= ~(0xff << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
return ctrl->reg_write(ctrl, reg, val);
}
static struct sdw_master_port_ops qcom_swrm_port_ops = {
.dpn_set_port_params = qcom_swrm_port_params,
.dpn_set_port_transport_params = qcom_swrm_transport_params,
.dpn_port_enable_ch = qcom_swrm_port_enable,
};
static struct sdw_master_ops qcom_swrm_ops = {
.xfer_msg = qcom_swrm_xfer_msg,
.pre_bank_switch = qcom_swrm_pre_bank_switch,
};
static int qcom_swrm_compute_params(struct sdw_bus *bus)
{
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
struct sdw_master_runtime *m_rt;
struct sdw_slave_runtime *s_rt;
struct sdw_port_runtime *p_rt;
struct qcom_swrm_port_config *pcfg;
int i = 0;
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
pcfg = &ctrl->pconfig[p_rt->num - 1];
p_rt->transport_params.port_num = p_rt->num;
p_rt->transport_params.sample_interval = pcfg->si + 1;
p_rt->transport_params.offset1 = pcfg->off1;
p_rt->transport_params.offset2 = pcfg->off2;
}
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
pcfg = &ctrl->pconfig[i];
p_rt->transport_params.port_num = p_rt->num;
p_rt->transport_params.sample_interval =
pcfg->si + 1;
p_rt->transport_params.offset1 = pcfg->off1;
p_rt->transport_params.offset2 = pcfg->off2;
i++;
}
}
}
return 0;
}
static u32 qcom_swrm_freq_tbl[MAX_FREQ_NUM] = {
DEFAULT_CLK_FREQ,
};
static void qcom_swrm_slave_wq(struct work_struct *work)
{
struct qcom_swrm_ctrl *ctrl =
container_of(work, struct qcom_swrm_ctrl, slave_work);
qcom_swrm_get_device_status(ctrl);
sdw_handle_slave_status(&ctrl->bus, ctrl->status);
}
static void qcom_swrm_stream_free_ports(struct qcom_swrm_ctrl *ctrl,
struct sdw_stream_runtime *stream)
{
struct sdw_master_runtime *m_rt;
struct sdw_port_runtime *p_rt;
unsigned long *port_mask;
mutex_lock(&ctrl->port_lock);
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
if (m_rt->direction == SDW_DATA_DIR_RX)
port_mask = &ctrl->dout_port_mask;
else
port_mask = &ctrl->din_port_mask;
list_for_each_entry(p_rt, &m_rt->port_list, port_node)
clear_bit(p_rt->num - 1, port_mask);
}
mutex_unlock(&ctrl->port_lock);
}
static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl,
struct sdw_stream_runtime *stream,
struct snd_pcm_hw_params *params,
int direction)
{
struct sdw_port_config pconfig[QCOM_SDW_MAX_PORTS];
struct sdw_stream_config sconfig;
struct sdw_master_runtime *m_rt;
struct sdw_slave_runtime *s_rt;
struct sdw_port_runtime *p_rt;
unsigned long *port_mask;
int i, maxport, pn, nports = 0, ret = 0;
mutex_lock(&ctrl->port_lock);
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
if (m_rt->direction == SDW_DATA_DIR_RX) {
maxport = ctrl->num_dout_ports;
port_mask = &ctrl->dout_port_mask;
} else {
maxport = ctrl->num_din_ports;
port_mask = &ctrl->din_port_mask;
}
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
/* Port numbers start from 1 - 14*/
pn = find_first_zero_bit(port_mask, maxport);
if (pn > (maxport - 1)) {
dev_err(ctrl->dev, "All ports busy\n");
ret = -EBUSY;
goto err;
}
set_bit(pn, port_mask);
pconfig[nports].num = pn + 1;
pconfig[nports].ch_mask = p_rt->ch_mask;
nports++;
}
}
}
if (direction == SNDRV_PCM_STREAM_CAPTURE)
sconfig.direction = SDW_DATA_DIR_TX;
else
sconfig.direction = SDW_DATA_DIR_RX;
/* hw parameters wil be ignored as we only support PDM */
sconfig.ch_count = 1;
sconfig.frame_rate = params_rate(params);
sconfig.type = stream->type;
sconfig.bps = 1;
sdw_stream_add_master(&ctrl->bus, &sconfig, pconfig,
nports, stream);
err:
if (ret) {
for (i = 0; i < nports; i++)
clear_bit(pconfig[i].num - 1, port_mask);
}
mutex_unlock(&ctrl->port_lock);
return ret;
}
static int qcom_swrm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);
struct sdw_stream_runtime *sruntime = ctrl->sruntime[dai->id];
int ret;
ret = qcom_swrm_stream_alloc_ports(ctrl, sruntime, params,
substream->stream);
if (ret)
qcom_swrm_stream_free_ports(ctrl, sruntime);
return ret;
}
static int qcom_swrm_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);
struct sdw_stream_runtime *sruntime = ctrl->sruntime[dai->id];
qcom_swrm_stream_free_ports(ctrl, sruntime);
sdw_stream_remove_master(&ctrl->bus, sruntime);
return 0;
}
static int qcom_swrm_set_sdw_stream(struct snd_soc_dai *dai,
void *stream, int direction)
{
struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);
ctrl->sruntime[dai->id] = stream;
return 0;
}
static int qcom_swrm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sdw_stream_runtime *sruntime;
int ret, i;
sruntime = sdw_alloc_stream(dai->name);
if (!sruntime)
return -ENOMEM;
ctrl->sruntime[dai->id] = sruntime;
for (i = 0; i < rtd->num_codecs; i++) {
ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sruntime,
substream->stream);
if (ret < 0 && ret != -ENOTSUPP) {
dev_err(dai->dev, "Failed to set sdw stream on %s",
rtd->codec_dais[i]->name);
sdw_release_stream(sruntime);
return ret;
}
}
return 0;
}
static void qcom_swrm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);
sdw_release_stream(ctrl->sruntime[dai->id]);
ctrl->sruntime[dai->id] = NULL;
}
static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = {
.hw_params = qcom_swrm_hw_params,
.hw_free = qcom_swrm_hw_free,
.startup = qcom_swrm_startup,
.shutdown = qcom_swrm_shutdown,
.set_sdw_stream = qcom_swrm_set_sdw_stream,
};
static const struct snd_soc_component_driver qcom_swrm_dai_component = {
.name = "soundwire",
};
static int qcom_swrm_register_dais(struct qcom_swrm_ctrl *ctrl)
{
int num_dais = ctrl->num_dout_ports + ctrl->num_din_ports;
struct snd_soc_dai_driver *dais;
struct snd_soc_pcm_stream *stream;
struct device *dev = ctrl->dev;
int i;
/* PDM dais are only tested for now */
dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
if (!dais)
return -ENOMEM;
for (i = 0; i < num_dais; i++) {
dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW Pin%d", i);
if (!dais[i].name)
return -ENOMEM;
if (i < ctrl->num_dout_ports)
stream = &dais[i].playback;
else
stream = &dais[i].capture;
stream->channels_min = 1;
stream->channels_max = 1;
stream->rates = SNDRV_PCM_RATE_48000;
stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
dais[i].ops = &qcom_swrm_pdm_dai_ops;
dais[i].id = i;
}
return devm_snd_soc_register_component(ctrl->dev,
&qcom_swrm_dai_component,
dais, num_dais);
}
static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl)
{
struct device_node *np = ctrl->dev->of_node;
u8 off1[QCOM_SDW_MAX_PORTS];
u8 off2[QCOM_SDW_MAX_PORTS];
u8 si[QCOM_SDW_MAX_PORTS];
int i, ret, nports, val;
ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val);
ctrl->num_dout_ports = val & SWRM_COMP_PARAMS_DOUT_PORTS_MASK;
ctrl->num_din_ports = (val & SWRM_COMP_PARAMS_DIN_PORTS_MASK) >> 5;
ret = of_property_read_u32(np, "qcom,din-ports", &val);
if (ret)
return ret;
if (val > ctrl->num_din_ports)
return -EINVAL;
ctrl->num_din_ports = val;
ret = of_property_read_u32(np, "qcom,dout-ports", &val);
if (ret)
return ret;
if (val > ctrl->num_dout_ports)
return -EINVAL;
ctrl->num_dout_ports = val;
nports = ctrl->num_dout_ports + ctrl->num_din_ports;
ret = of_property_read_u8_array(np, "qcom,ports-offset1",
off1, nports);
if (ret)
return ret;
ret = of_property_read_u8_array(np, "qcom,ports-offset2",
off2, nports);
if (ret)
return ret;
ret = of_property_read_u8_array(np, "qcom,ports-sinterval-low",
si, nports);
if (ret)
return ret;
for (i = 0; i < nports; i++) {
ctrl->pconfig[i].si = si[i];
ctrl->pconfig[i].off1 = off1[i];
ctrl->pconfig[i].off2 = off2[i];
}
return 0;
}
static int qcom_swrm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sdw_master_prop *prop;
struct sdw_bus_params *params;
struct qcom_swrm_ctrl *ctrl;
int ret;
u32 val;
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return -ENOMEM;
if (dev->parent->bus == &slimbus_bus) {
ctrl->reg_read = qcom_swrm_abh_reg_read;
ctrl->reg_write = qcom_swrm_ahb_reg_write;
ctrl->regmap = dev_get_regmap(dev->parent, NULL);
if (!ctrl->regmap)
return -EINVAL;
} else {
/* Only WCD based SoundWire controller is supported */
return -ENOTSUPP;
}
ctrl->irq = of_irq_get(dev->of_node, 0);
if (ctrl->irq < 0)
return ctrl->irq;
ctrl->hclk = devm_clk_get(dev, "iface");
if (IS_ERR(ctrl->hclk))
return PTR_ERR(ctrl->hclk);
clk_prepare_enable(ctrl->hclk);
ctrl->dev = dev;
dev_set_drvdata(&pdev->dev, ctrl);
spin_lock_init(&ctrl->comp_lock);
mutex_init(&ctrl->port_lock);
INIT_WORK(&ctrl->slave_work, qcom_swrm_slave_wq);
ctrl->bus.dev = dev;
ctrl->bus.ops = &qcom_swrm_ops;
ctrl->bus.port_ops = &qcom_swrm_port_ops;
ctrl->bus.compute_params = &qcom_swrm_compute_params;
ret = qcom_swrm_get_port_config(ctrl);
if (ret)
return ret;
params = &ctrl->bus.params;
params->max_dr_freq = DEFAULT_CLK_FREQ;
params->curr_dr_freq = DEFAULT_CLK_FREQ;
params->col = SWRM_DEFAULT_COL;
params->row = SWRM_DEFAULT_ROWS;
ctrl->reg_read(ctrl, SWRM_MCP_STATUS, &val);
params->curr_bank = val & SWRM_MCP_STATUS_BANK_NUM_MASK;
params->next_bank = !params->curr_bank;
prop = &ctrl->bus.prop;
prop->max_clk_freq = DEFAULT_CLK_FREQ;
prop->num_clk_gears = 0;
prop->num_clk_freq = MAX_FREQ_NUM;
prop->clk_freq = &qcom_swrm_freq_tbl[0];
prop->default_col = SWRM_DEFAULT_COL;
prop->default_row = SWRM_DEFAULT_ROWS;
ctrl->reg_read(ctrl, SWRM_COMP_HW_VERSION, &ctrl->version);
ret = devm_request_threaded_irq(dev, ctrl->irq, NULL,
qcom_swrm_irq_handler,
IRQF_TRIGGER_RISING,
"soundwire", ctrl);
if (ret) {
dev_err(dev, "Failed to request soundwire irq\n");
goto err;
}
ret = sdw_add_bus_master(&ctrl->bus);
if (ret) {
dev_err(dev, "Failed to register Soundwire controller (%d)\n",
ret);
goto err;
}
qcom_swrm_init(ctrl);
ret = qcom_swrm_register_dais(ctrl);
if (ret)
goto err;
dev_info(dev, "Qualcomm Soundwire controller v%x.%x.%x Registered\n",
(ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff,
ctrl->version & 0xffff);
return 0;
err:
clk_disable_unprepare(ctrl->hclk);
return ret;
}
static int qcom_swrm_remove(struct platform_device *pdev)
{
struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
sdw_delete_bus_master(&ctrl->bus);
clk_disable_unprepare(ctrl->hclk);
return 0;
}
static const struct of_device_id qcom_swrm_of_match[] = {
{ .compatible = "qcom,soundwire-v1.3.0", },
{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, qcom_swrm_of_match);
static struct platform_driver qcom_swrm_driver = {
.probe = &qcom_swrm_probe,
.remove = &qcom_swrm_remove,
.driver = {
.name = "qcom-soundwire",
.of_match_table = qcom_swrm_of_match,
}
};
module_platform_driver(qcom_swrm_driver);
MODULE_DESCRIPTION("Qualcomm soundwire driver");
MODULE_LICENSE("GPL v2");

View File

@ -1554,8 +1554,6 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream);
ret = _sdw_prepare_stream(stream);
if (ret < 0)
pr_err("Prepare for stream:%s failed: %d\n", stream->name, ret);
sdw_release_bus_lock(stream);
return ret;
@ -1622,8 +1620,6 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream);
ret = _sdw_enable_stream(stream);
if (ret < 0)
pr_err("Enable for stream:%s failed: %d\n", stream->name, ret);
sdw_release_bus_lock(stream);
return ret;
@ -1698,8 +1694,6 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream);
ret = _sdw_disable_stream(stream);
if (ret < 0)
pr_err("Disable for stream:%s failed: %d\n", stream->name, ret);
sdw_release_bus_lock(stream);
return ret;
@ -1756,8 +1750,6 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream);
ret = _sdw_deprepare_stream(stream);
if (ret < 0)
pr_err("De-prepare for stream:%d failed: %d\n", ret, ret);
sdw_release_bus_lock(stream);
return ret;

View File

@ -132,11 +132,13 @@ static int uio_dmem_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
if (irq_on) {
if (test_and_clear_bit(0, &priv->flags))
enable_irq(dev_info->irq);
spin_unlock_irqrestore(&priv->lock, flags);
} else {
if (!test_and_set_bit(0, &priv->flags))
if (!test_and_set_bit(0, &priv->flags)) {
spin_unlock_irqrestore(&priv->lock, flags);
disable_irq(dev_info->irq);
}
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}

View File

@ -156,6 +156,8 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
uioinfo->irq = ret;
if (ret == -ENXIO && pdev->dev.of_node)
uioinfo->irq = UIO_IRQ_NONE;
else if (ret == -EPROBE_DEFER)
return ret;
else if (ret < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
return ret;

View File

@ -1210,14 +1210,17 @@ static void setup_crash_devices_work_queue(struct work_struct *work)
{
struct controlvm_message local_crash_bus_msg;
struct controlvm_message local_crash_dev_msg;
struct controlvm_message msg;
struct controlvm_message msg = {
.hdr.id = CONTROLVM_CHIPSET_INIT,
.cmd.init_chipset = {
.bus_count = 23,
.switch_count = 0,
},
};
u32 local_crash_msg_offset;
u16 local_crash_msg_count;
/* send init chipset msg */
msg.hdr.id = CONTROLVM_CHIPSET_INIT;
msg.cmd.init_chipset.bus_count = 23;
msg.cmd.init_chipset.switch_count = 0;
chipset_init(&msg);
/* get saved message count */
if (visorchannel_read(chipset_dev->controlvm_channel,

View File

@ -414,8 +414,9 @@ static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr,
}
}
static u8 fake_vmeread8(struct fake_driver *bridge, unsigned long long addr,
u32 aspace, u32 cycle)
static noinline_for_stack u8 fake_vmeread8(struct fake_driver *bridge,
unsigned long long addr,
u32 aspace, u32 cycle)
{
u8 retval = 0xff;
int i;
@ -446,8 +447,9 @@ static u8 fake_vmeread8(struct fake_driver *bridge, unsigned long long addr,
return retval;
}
static u16 fake_vmeread16(struct fake_driver *bridge, unsigned long long addr,
u32 aspace, u32 cycle)
static noinline_for_stack u16 fake_vmeread16(struct fake_driver *bridge,
unsigned long long addr,
u32 aspace, u32 cycle)
{
u16 retval = 0xffff;
int i;
@ -478,8 +480,9 @@ static u16 fake_vmeread16(struct fake_driver *bridge, unsigned long long addr,
return retval;
}
static u32 fake_vmeread32(struct fake_driver *bridge, unsigned long long addr,
u32 aspace, u32 cycle)
static noinline_for_stack u32 fake_vmeread32(struct fake_driver *bridge,
unsigned long long addr,
u32 aspace, u32 cycle)
{
u32 retval = 0xffffffff;
int i;
@ -609,8 +612,9 @@ static ssize_t fake_master_read(struct vme_master_resource *image, void *buf,
return retval;
}
static void fake_vmewrite8(struct fake_driver *bridge, u8 *buf,
unsigned long long addr, u32 aspace, u32 cycle)
static noinline_for_stack void fake_vmewrite8(struct fake_driver *bridge,
u8 *buf, unsigned long long addr,
u32 aspace, u32 cycle)
{
int i;
unsigned long long start, end, offset;
@ -639,8 +643,9 @@ static void fake_vmewrite8(struct fake_driver *bridge, u8 *buf,
}
static void fake_vmewrite16(struct fake_driver *bridge, u16 *buf,
unsigned long long addr, u32 aspace, u32 cycle)
static noinline_for_stack void fake_vmewrite16(struct fake_driver *bridge,
u16 *buf, unsigned long long addr,
u32 aspace, u32 cycle)
{
int i;
unsigned long long start, end, offset;
@ -669,8 +674,9 @@ static void fake_vmewrite16(struct fake_driver *bridge, u16 *buf,
}
static void fake_vmewrite32(struct fake_driver *bridge, u32 *buf,
unsigned long long addr, u32 aspace, u32 cycle)
static noinline_for_stack void fake_vmewrite32(struct fake_driver *bridge,
u32 *buf, unsigned long long addr,
u32 aspace, u32 cycle)
{
int i;
unsigned long long start, end, offset;

View File

@ -38,12 +38,6 @@
#define OMAP_HDQ_INT_STATUS_TXCOMPLETE BIT(2)
#define OMAP_HDQ_INT_STATUS_RXCOMPLETE BIT(1)
#define OMAP_HDQ_INT_STATUS_TIMEOUT BIT(0)
#define OMAP_HDQ_SYSCONFIG 0x14
#define OMAP_HDQ_SYSCONFIG_SOFTRESET BIT(1)
#define OMAP_HDQ_SYSCONFIG_AUTOIDLE BIT(0)
#define OMAP_HDQ_SYSCONFIG_NOIDLE 0x0
#define OMAP_HDQ_SYSSTATUS 0x18
#define OMAP_HDQ_SYSSTATUS_RESETDONE BIT(0)
#define OMAP_HDQ_FLAG_CLEAR 0
#define OMAP_HDQ_FLAG_SET 1
@ -62,17 +56,9 @@ struct hdq_data {
void __iomem *hdq_base;
/* lock status update */
struct mutex hdq_mutex;
int hdq_usecount;
u8 hdq_irqstatus;
/* device lock */
spinlock_t hdq_spinlock;
/*
* Used to control the call to omap_hdq_get and omap_hdq_put.
* HDQ Protocol: Write the CMD|REG_address first, followed by
* the data wrire or read.
*/
int init_trans;
int rrw;
/* mode: 0-HDQ 1-W1 */
int mode;
@ -99,15 +85,6 @@ static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset,
return new_val;
}
static void hdq_disable_interrupt(struct hdq_data *hdq_data, u32 offset,
u32 mask)
{
u32 ie;
ie = readl(hdq_data->hdq_base + offset);
writel(ie & mask, hdq_data->hdq_base + offset);
}
/*
* Wait for one or more bits in flag change.
* HDQ_FLAG_SET: wait until any bit in the flag is set.
@ -142,22 +119,24 @@ static int hdq_wait_for_flag(struct hdq_data *hdq_data, u32 offset,
return ret;
}
/* Clear saved irqstatus after using an interrupt */
static void hdq_reset_irqstatus(struct hdq_data *hdq_data)
{
unsigned long irqflags;
spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
hdq_data->hdq_irqstatus = 0;
spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
}
/* write out a byte and fill *status with HDQ_INT_STATUS */
static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)
{
int ret;
u8 tmp_status;
unsigned long irqflags;
*status = 0;
spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
/* clear interrupt flags via a dummy read */
hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
/* ISR loads it with new INT_STATUS */
hdq_data->hdq_irqstatus = 0;
spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
hdq_reg_out(hdq_data, OMAP_HDQ_TX_DATA, val);
/* set the GO bit */
@ -191,6 +170,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)
}
out:
hdq_reset_irqstatus(hdq_data);
return ret;
}
@ -237,47 +217,11 @@ static void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev,
slave_found(master_dev, id);
}
static int _omap_hdq_reset(struct hdq_data *hdq_data)
{
int ret;
u8 tmp_status;
hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG,
OMAP_HDQ_SYSCONFIG_SOFTRESET);
/*
* Select HDQ/1W mode & enable clocks.
* It is observed that INT flags can't be cleared via a read and GO/INIT
* won't return to zero if interrupt is disabled. So we always enable
* interrupt.
*/
hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
/* wait for reset to complete */
ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_SYSSTATUS,
OMAP_HDQ_SYSSTATUS_RESETDONE, OMAP_HDQ_FLAG_SET, &tmp_status);
if (ret)
dev_dbg(hdq_data->dev, "timeout waiting HDQ reset, %x",
tmp_status);
else {
hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK |
hdq_data->mode);
hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG,
OMAP_HDQ_SYSCONFIG_AUTOIDLE);
}
return ret;
}
/* Issue break pulse to the device */
static int omap_hdq_break(struct hdq_data *hdq_data)
{
int ret = 0;
u8 tmp_status;
unsigned long irqflags;
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
@ -286,13 +230,6 @@ static int omap_hdq_break(struct hdq_data *hdq_data)
goto rtn;
}
spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
/* clear interrupt flags via a dummy read */
hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
/* ISR loads it with new INT_STATUS */
hdq_data->hdq_irqstatus = 0;
spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
/* set the INIT and GO bit */
hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS,
OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO,
@ -341,6 +278,7 @@ static int omap_hdq_break(struct hdq_data *hdq_data)
" return to zero, %x", tmp_status);
out:
hdq_reset_irqstatus(hdq_data);
mutex_unlock(&hdq_data->hdq_mutex);
rtn:
return ret;
@ -357,7 +295,7 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)
goto rtn;
}
if (!hdq_data->hdq_usecount) {
if (pm_runtime_suspended(hdq_data->dev)) {
ret = -EINVAL;
goto out;
}
@ -388,86 +326,13 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)
/* the data is ready. Read it in! */
*val = hdq_reg_in(hdq_data, OMAP_HDQ_RX_DATA);
out:
hdq_reset_irqstatus(hdq_data);
mutex_unlock(&hdq_data->hdq_mutex);
rtn:
return ret;
}
/* Enable clocks and set the controller to HDQ/1W mode */
static int omap_hdq_get(struct hdq_data *hdq_data)
{
int ret = 0;
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
ret = -EINTR;
goto rtn;
}
if (OMAP_HDQ_MAX_USER == hdq_data->hdq_usecount) {
dev_dbg(hdq_data->dev, "attempt to exceed the max use count");
ret = -EINVAL;
goto out;
} else {
hdq_data->hdq_usecount++;
try_module_get(THIS_MODULE);
if (1 == hdq_data->hdq_usecount) {
pm_runtime_get_sync(hdq_data->dev);
/* make sure HDQ/1W is out of reset */
if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) &
OMAP_HDQ_SYSSTATUS_RESETDONE)) {
ret = _omap_hdq_reset(hdq_data);
if (ret)
/* back up the count */
hdq_data->hdq_usecount--;
} else {
/* select HDQ/1W mode & enable clocks */
hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK |
hdq_data->mode);
hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG,
OMAP_HDQ_SYSCONFIG_NOIDLE);
hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
}
}
}
out:
mutex_unlock(&hdq_data->hdq_mutex);
rtn:
return ret;
}
/* Disable clocks to the module */
static int omap_hdq_put(struct hdq_data *hdq_data)
{
int ret = 0;
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0)
return -EINTR;
hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG,
OMAP_HDQ_SYSCONFIG_AUTOIDLE);
if (0 == hdq_data->hdq_usecount) {
dev_dbg(hdq_data->dev, "attempt to decrement use count"
" when it is zero");
ret = -EINVAL;
} else {
hdq_data->hdq_usecount--;
module_put(THIS_MODULE);
if (0 == hdq_data->hdq_usecount)
pm_runtime_put_sync(hdq_data->dev);
}
mutex_unlock(&hdq_data->hdq_mutex);
return ret;
}
/*
* W1 triplet callback function - used for searching ROM addresses.
* Registered only when controller is in 1-wire mode.
@ -482,7 +347,12 @@ static u8 omap_w1_triplet(void *_hdq, u8 bdir)
OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK;
u8 mask = ctrl | OMAP_HDQ_CTRL_STATUS_DIR;
omap_hdq_get(_hdq);
err = pm_runtime_get_sync(hdq_data->dev);
if (err < 0) {
pm_runtime_put_noidle(hdq_data->dev);
return err;
}
err = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (err < 0) {
@ -490,7 +360,6 @@ static u8 omap_w1_triplet(void *_hdq, u8 bdir)
goto rtn;
}
hdq_data->hdq_irqstatus = 0;
/* read id_bit */
hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS,
ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask);
@ -504,7 +373,9 @@ static u8 omap_w1_triplet(void *_hdq, u8 bdir)
}
id_bit = (hdq_reg_in(_hdq, OMAP_HDQ_RX_DATA) & 0x01);
hdq_data->hdq_irqstatus = 0;
/* Must clear irqstatus for another RXCOMPLETE interrupt */
hdq_reset_irqstatus(hdq_data);
/* read comp_bit */
hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS,
ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask);
@ -547,18 +418,33 @@ static u8 omap_w1_triplet(void *_hdq, u8 bdir)
OMAP_HDQ_CTRL_STATUS_SINGLE);
out:
hdq_reset_irqstatus(hdq_data);
mutex_unlock(&hdq_data->hdq_mutex);
rtn:
omap_hdq_put(_hdq);
pm_runtime_mark_last_busy(hdq_data->dev);
pm_runtime_put_autosuspend(hdq_data->dev);
return ret;
}
/* reset callback */
static u8 omap_w1_reset_bus(void *_hdq)
{
omap_hdq_get(_hdq);
omap_hdq_break(_hdq);
omap_hdq_put(_hdq);
struct hdq_data *hdq_data = _hdq;
int err;
err = pm_runtime_get_sync(hdq_data->dev);
if (err < 0) {
pm_runtime_put_noidle(hdq_data->dev);
return err;
}
omap_hdq_break(hdq_data);
pm_runtime_mark_last_busy(hdq_data->dev);
pm_runtime_put_autosuspend(hdq_data->dev);
return 0;
}
@ -569,37 +455,19 @@ static u8 omap_w1_read_byte(void *_hdq)
u8 val = 0;
int ret;
/* First write to initialize the transfer */
if (hdq_data->init_trans == 0)
omap_hdq_get(hdq_data);
ret = pm_runtime_get_sync(hdq_data->dev);
if (ret < 0) {
pm_runtime_put_noidle(hdq_data->dev);
ret = hdq_read_byte(hdq_data, &val);
if (ret) {
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
return -EINTR;
}
hdq_data->init_trans = 0;
mutex_unlock(&hdq_data->hdq_mutex);
omap_hdq_put(hdq_data);
return -1;
}
hdq_disable_interrupt(hdq_data, OMAP_HDQ_CTRL_STATUS,
~OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
ret = hdq_read_byte(hdq_data, &val);
if (ret)
ret = -1;
/* Write followed by a read, release the module */
if (hdq_data->init_trans) {
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
return -EINTR;
}
hdq_data->init_trans = 0;
mutex_unlock(&hdq_data->hdq_mutex);
omap_hdq_put(hdq_data);
}
pm_runtime_mark_last_busy(hdq_data->dev);
pm_runtime_put_autosuspend(hdq_data->dev);
return val;
}
@ -611,9 +479,12 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)
int ret;
u8 status;
/* First write to initialize the transfer */
if (hdq_data->init_trans == 0)
omap_hdq_get(hdq_data);
ret = pm_runtime_get_sync(hdq_data->dev);
if (ret < 0) {
pm_runtime_put_noidle(hdq_data->dev);
return;
}
/*
* We need to reset the slave before
@ -623,31 +494,15 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)
if (byte == W1_SKIP_ROM)
omap_hdq_break(hdq_data);
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
return;
}
hdq_data->init_trans++;
mutex_unlock(&hdq_data->hdq_mutex);
ret = hdq_write_byte(hdq_data, byte, &status);
if (ret < 0) {
dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status);
return;
goto out_err;
}
/* Second write, data transferred. Release the module */
if (hdq_data->init_trans > 1) {
omap_hdq_put(hdq_data);
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
return;
}
hdq_data->init_trans = 0;
mutex_unlock(&hdq_data->hdq_mutex);
}
out_err:
pm_runtime_mark_last_busy(hdq_data->dev);
pm_runtime_put_autosuspend(hdq_data->dev);
}
static struct w1_bus_master omap_w1_master = {
@ -656,6 +511,35 @@ static struct w1_bus_master omap_w1_master = {
.reset_bus = omap_w1_reset_bus,
};
static int __maybe_unused omap_hdq_runtime_suspend(struct device *dev)
{
struct hdq_data *hdq_data = dev_get_drvdata(dev);
hdq_reg_out(hdq_data, 0, hdq_data->mode);
hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
return 0;
}
static int __maybe_unused omap_hdq_runtime_resume(struct device *dev)
{
struct hdq_data *hdq_data = dev_get_drvdata(dev);
/* select HDQ/1W mode & enable clocks */
hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK |
hdq_data->mode);
hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
return 0;
}
static const struct dev_pm_ops omap_hdq_pm_ops = {
SET_RUNTIME_PM_OPS(omap_hdq_runtime_suspend,
omap_hdq_runtime_resume, NULL)
};
static int omap_hdq_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -677,21 +561,25 @@ static int omap_hdq_probe(struct platform_device *pdev)
if (IS_ERR(hdq_data->hdq_base))
return PTR_ERR(hdq_data->hdq_base);
hdq_data->hdq_usecount = 0;
hdq_data->rrw = 0;
mutex_init(&hdq_data->hdq_mutex);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
dev_dbg(&pdev->dev, "pm_runtime_get_sync failed\n");
goto err_w1;
ret = of_property_read_string(pdev->dev.of_node, "ti,mode", &mode);
if (ret < 0 || !strcmp(mode, "hdq")) {
hdq_data->mode = 0;
omap_w1_master.search = omap_w1_search_bus;
} else {
hdq_data->mode = 1;
omap_w1_master.triplet = omap_w1_triplet;
}
ret = _omap_hdq_reset(hdq_data);
if (ret) {
dev_dbg(&pdev->dev, "reset failed\n");
goto err_irq;
pm_runtime_enable(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
pm_runtime_put_noidle(&pdev->dev);
dev_dbg(&pdev->dev, "pm_runtime_get_sync failed\n");
goto err_w1;
}
rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION);
@ -715,16 +603,8 @@ static int omap_hdq_probe(struct platform_device *pdev)
omap_hdq_break(hdq_data);
pm_runtime_put_sync(&pdev->dev);
ret = of_property_read_string(pdev->dev.of_node, "ti,mode", &mode);
if (ret < 0 || !strcmp(mode, "hdq")) {
hdq_data->mode = 0;
omap_w1_master.search = omap_w1_search_bus;
} else {
hdq_data->mode = 1;
omap_w1_master.triplet = omap_w1_triplet;
}
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
omap_w1_master.data = hdq_data;
@ -739,6 +619,7 @@ static int omap_hdq_probe(struct platform_device *pdev)
err_irq:
pm_runtime_put_sync(&pdev->dev);
err_w1:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
@ -746,23 +627,19 @@ static int omap_hdq_probe(struct platform_device *pdev)
static int omap_hdq_remove(struct platform_device *pdev)
{
struct hdq_data *hdq_data = platform_get_drvdata(pdev);
int active;
mutex_lock(&hdq_data->hdq_mutex);
if (hdq_data->hdq_usecount) {
dev_dbg(&pdev->dev, "removed when use count is not zero\n");
mutex_unlock(&hdq_data->hdq_mutex);
return -EBUSY;
}
mutex_unlock(&hdq_data->hdq_mutex);
/* remove module dependency */
pm_runtime_disable(&pdev->dev);
active = pm_runtime_get_sync(&pdev->dev);
if (active < 0)
pm_runtime_put_noidle(&pdev->dev);
w1_remove_master_device(&omap_w1_master);
pm_runtime_dont_use_autosuspend(&pdev->dev);
if (active >= 0)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
@ -779,6 +656,7 @@ static struct platform_driver omap_hdq_driver = {
.driver = {
.name = "omap_hdq",
.of_match_table = omap_hdq_dt_ids,
.pm = &omap_hdq_pm_ops,
},
};
module_platform_driver(omap_hdq_driver);

View File

@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Qualcomm interconnect IDs
*
* Copyright (c) 2019, Linaro Ltd.
* Author: Georgi Djakov <georgi.djakov@linaro.org>
*/
#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8916_H
#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8916_H
#define BIMC_SNOC_SLV 0
#define MASTER_JPEG 1
#define MASTER_MDP_PORT0 2
#define MASTER_QDSS_BAM 3
#define MASTER_QDSS_ETR 4
#define MASTER_SNOC_CFG 5
#define MASTER_VFE 6
#define MASTER_VIDEO_P0 7
#define SNOC_MM_INT_0 8
#define SNOC_MM_INT_1 9
#define SNOC_MM_INT_2 10
#define SNOC_MM_INT_BIMC 11
#define PCNOC_SNOC_SLV 12
#define SLAVE_APSS 13
#define SLAVE_CATS_128 14
#define SLAVE_OCMEM_64 15
#define SLAVE_IMEM 16
#define SLAVE_QDSS_STM 17
#define SLAVE_SRVC_SNOC 18
#define SNOC_BIMC_0_MAS 19
#define SNOC_BIMC_1_MAS 20
#define SNOC_INT_0 21
#define SNOC_INT_1 22
#define SNOC_INT_BIMC 23
#define SNOC_PCNOC_MAS 24
#define SNOC_QDSS_INT 25
#define BIMC_SNOC_MAS 0
#define MASTER_AMPSS_M0 1
#define MASTER_GRAPHICS_3D 2
#define MASTER_TCU0 3
#define MASTER_TCU1 4
#define SLAVE_AMPSS_L2 5
#define SLAVE_EBI_CH0 6
#define SNOC_BIMC_0_SLV 7
#define SNOC_BIMC_1_SLV 8
#define MASTER_BLSP_1 0
#define MASTER_DEHR 1
#define MASTER_LPASS 2
#define MASTER_CRYPTO_CORE0 3
#define MASTER_SDCC_1 4
#define MASTER_SDCC_2 5
#define MASTER_SPDM 6
#define MASTER_USB_HS 7
#define PCNOC_INT_0 8
#define PCNOC_INT_1 9
#define PCNOC_MAS_0 10
#define PCNOC_MAS_1 11
#define PCNOC_SLV_0 12
#define PCNOC_SLV_1 13
#define PCNOC_SLV_2 14
#define PCNOC_SLV_3 15
#define PCNOC_SLV_4 16
#define PCNOC_SLV_8 17
#define PCNOC_SLV_9 18
#define PCNOC_SNOC_MAS 19
#define SLAVE_BIMC_CFG 20
#define SLAVE_BLSP_1 21
#define SLAVE_BOOT_ROM 22
#define SLAVE_CAMERA_CFG 23
#define SLAVE_CLK_CTL 24
#define SLAVE_CRYPTO_0_CFG 25
#define SLAVE_DEHR_CFG 26
#define SLAVE_DISPLAY_CFG 27
#define SLAVE_GRAPHICS_3D_CFG 28
#define SLAVE_IMEM_CFG 29
#define SLAVE_LPASS 30
#define SLAVE_MPM 31
#define SLAVE_MSG_RAM 32
#define SLAVE_MSS 33
#define SLAVE_PDM 34
#define SLAVE_PMIC_ARB 35
#define SLAVE_PCNOC_CFG 36
#define SLAVE_PRNG 37
#define SLAVE_QDSS_CFG 38
#define SLAVE_RBCPR_CFG 39
#define SLAVE_SDCC_1 40
#define SLAVE_SDCC_2 41
#define SLAVE_SECURITY 42
#define SLAVE_SNOC_CFG 43
#define SLAVE_SPDM 44
#define SLAVE_TCSR 45
#define SLAVE_TLMM 46
#define SLAVE_USB_HS 47
#define SLAVE_VENUS_CFG 48
#define SNOC_PCNOC_SLV 49
#endif

View File

@ -17,6 +17,7 @@
#define PCI_ID_ALCOR_MICRO 0x1AEA
#define PCI_ID_AU6601 0x6601
#define PCI_ID_AU6621 0x6621
#define PCI_ID_AU6625 0x6625
#define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000)

View File

@ -170,7 +170,7 @@ struct extcon_dev;
* Following APIs get the connected state of each external connector.
* The 'id' argument indicates the defined external connector.
*/
extern int extcon_get_state(struct extcon_dev *edev, unsigned int id);
int extcon_get_state(struct extcon_dev *edev, unsigned int id);
/*
* Following APIs get the property of each external connector.
@ -181,10 +181,10 @@ extern int extcon_get_state(struct extcon_dev *edev, unsigned int id);
* for each external connector. They are used to get the capability of the
* property of each external connector based on the id and property.
*/
extern int extcon_get_property(struct extcon_dev *edev, unsigned int id,
int extcon_get_property(struct extcon_dev *edev, unsigned int id,
unsigned int prop,
union extcon_property_value *prop_val);
extern int extcon_get_property_capability(struct extcon_dev *edev,
int extcon_get_property_capability(struct extcon_dev *edev,
unsigned int id, unsigned int prop);
/*
@ -196,38 +196,38 @@ extern int extcon_get_property_capability(struct extcon_dev *edev,
* extcon_register_notifier_all(*edev, *nb) : Register a notifier block
* for all supported external connectors of the extcon.
*/
extern int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb);
extern int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb);
extern int devm_extcon_register_notifier(struct device *dev,
int devm_extcon_register_notifier(struct device *dev,
struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb);
extern void devm_extcon_unregister_notifier(struct device *dev,
void devm_extcon_unregister_notifier(struct device *dev,
struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb);
extern int extcon_register_notifier_all(struct extcon_dev *edev,
int extcon_register_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb);
extern int extcon_unregister_notifier_all(struct extcon_dev *edev,
int extcon_unregister_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb);
extern int devm_extcon_register_notifier_all(struct device *dev,
int devm_extcon_register_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb);
extern void devm_extcon_unregister_notifier_all(struct device *dev,
void devm_extcon_unregister_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb);
/*
* Following APIs get the extcon_dev from devicetree or by through extcon name.
*/
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
extern struct extcon_dev *extcon_find_edev_by_node(struct device_node *node);
extern struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev,
struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
struct extcon_dev *extcon_find_edev_by_node(struct device_node *node);
struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev,
int index);
/* Following API get the name of extcon device. */
extern const char *extcon_get_edev_name(struct extcon_dev *edev);
const char *extcon_get_edev_name(struct extcon_dev *edev);
#else /* CONFIG_EXTCON */
static inline int extcon_get_state(struct extcon_dev *edev, unsigned int id)

View File

@ -92,17 +92,26 @@ struct icc_node {
#if IS_ENABLED(CONFIG_INTERCONNECT)
int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
struct icc_node *icc_node_create(int id);
void icc_node_destroy(int id);
int icc_link_create(struct icc_node *node, const int dst_id);
int icc_link_destroy(struct icc_node *src, struct icc_node *dst);
void icc_node_add(struct icc_node *node, struct icc_provider *provider);
void icc_node_del(struct icc_node *node);
int icc_nodes_remove(struct icc_provider *provider);
int icc_provider_add(struct icc_provider *provider);
int icc_provider_del(struct icc_provider *provider);
#else
static inline int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
return -ENOTSUPP;
}
static inline struct icc_node *icc_node_create(int id)
{
return ERR_PTR(-ENOTSUPP);
@ -130,6 +139,11 @@ void icc_node_del(struct icc_node *node)
{
}
static inline int icc_nodes_remove(struct icc_provider *provider)
{
return -ENOTSUPP;
}
static inline int icc_provider_add(struct icc_provider *provider)
{
return -ENOTSUPP;

View File

@ -546,7 +546,8 @@ struct sdw_slave_ops {
* @debugfs: Slave debugfs
* @node: node for bus list
* @port_ready: Port ready completion flag for each Slave port
* @dev_num: Device Number assigned by Bus
* @dev_num: Current Device Number, values can be 0 or dev_num_sticky
* @dev_num_sticky: one-time static Device Number assigned by Bus
* @probed: boolean tracking driver state
* @probe_complete: completion utility to control potential races
* on startup between driver probe/initialization and SoundWire
@ -575,6 +576,7 @@ struct sdw_slave {
struct list_head node;
struct completion *port_ready;
u16 dev_num;
u16 dev_num_sticky;
bool probed;
struct completion probe_complete;
struct completion enumeration_complete;

View File

@ -5,6 +5,7 @@
#define __SDW_INTEL_H
#include <linux/irqreturn.h>
#include <linux/soundwire/sdw.h>
/**
* struct sdw_intel_stream_params_data: configuration passed during
@ -93,6 +94,11 @@ struct sdw_intel_link_res;
*/
#define SDW_INTEL_CLK_STOP_BUS_RESET BIT(3)
struct sdw_intel_slave_id {
int link_id;
struct sdw_slave_id id;
};
/**
* struct sdw_intel_ctx - context allocated by the controller
* driver probe
@ -101,9 +107,12 @@ struct sdw_intel_link_res;
* hardware capabilities after all power dependencies are settled.
* @link_mask: bit-wise mask listing SoundWire links reported by the
* Controller
* @num_slaves: total number of devices exposed across all enabled links
* @handle: ACPI parent handle
* @links: information for each link (controller-specific and kept
* opaque here)
* @ids: array of slave_id, representing Slaves exposed across all enabled
* links
* @link_list: list to handle interrupts across all links
* @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
*/
@ -111,8 +120,10 @@ struct sdw_intel_ctx {
int count;
void __iomem *mmio_base;
u32 link_mask;
int num_slaves;
acpi_handle handle;
struct sdw_intel_link_res *links;
struct sdw_intel_slave_id *ids;
struct list_head link_list;
struct mutex shim_lock; /* lock for access to shared SHIM registers */
};

View File

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef __PVPANIC_H__
#define __PVPANIC_H__
#define PVPANIC_PANICKED (1 << 0)
#define PVPANIC_CRASH_LOADED (1 << 1)
#endif /* __PVPANIC_H__ */