1
0
Fork 0

Char / Misc driver update for 4.7-rc1

Here's the big char and misc driver update for 4.7-rc1.
 
 Lots of different tiny driver subsystems have updates here with new
 drivers and functionality.  Details in the shortlog.
 
 All have been in linux-next with no reported issues for a while.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlc/0YYACgkQMUfUDdst+ynmtACeLpLLKZsy1v7WfkW92cLSOPBD
 2C8AoLFPKoh55rlOJrNz3bW9ANAaOloX
 =/nsL
 -----END PGP SIGNATURE-----

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

Pull char / misc driver updates from Greg KH:
 "Here's the big char and misc driver update for 4.7-rc1.

  Lots of different tiny driver subsystems have updates here with new
  drivers and functionality.  Details in the shortlog.

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

* tag 'char-misc-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (125 commits)
  mcb: Delete num_cells variable which is not required
  mcb: Fixed bar number assignment for the gdd
  mcb: Replace ioremap and request_region with the devm version
  mcb: Implement bus->dev.release callback
  mcb: export bus information via sysfs
  mcb: Correctly initialize the bus's device
  mei: bus: call mei_cl_read_start under device lock
  coresight: etb10: adjust read pointer only when needed
  coresight: configuring ETF in FIFO mode when acting as link
  coresight: tmc: implementing TMC-ETF AUX space API
  coresight: moving struct cs_buffers to header file
  coresight: tmc: keep track of memory width
  coresight: tmc: make sysFS and Perf mode mutually exclusive
  coresight: tmc: dump system memory content only when needed
  coresight: tmc: adding mode of operation for link/sinks
  coresight: tmc: getting rid of multiple read access
  coresight: tmc: allocating memory when needed
  coresight: tmc: making prepare/unprepare functions generic
  coresight: tmc: splitting driver in ETB/ETF and ETR components
  coresight: tmc: cleaning up header file
  ...
hifive-unleashed-5.1
Linus Torvalds 2016-05-20 21:20:31 -07:00
commit 5af2344013
96 changed files with 6422 additions and 3778 deletions

View File

@ -6,13 +6,6 @@ Description: (RW) Add/remove a sink from a trace path. There can be multiple
source for a single sink.
ex: echo 1 > /sys/bus/coresight/devices/20010000.etb/enable_sink
What: /sys/bus/coresight/devices/<memory_map>.etb/status
Date: November 2014
KernelVersion: 3.19
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) List various control and status registers. The specific
layout and content is driver specific.
What: /sys/bus/coresight/devices/<memory_map>.etb/trigger_cntr
Date: November 2014
KernelVersion: 3.19
@ -22,3 +15,65 @@ Description: (RW) Disables write access to the Trace RAM by stopping the
following the trigger event. The number of 32-bit words written
into the Trace RAM following the trigger event is equal to the
value stored in this register+1 (from ARM ETB-TRM).
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/rdp
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Defines the depth, in words, of the trace RAM in powers of
2. The value is read directly from HW register RDP, 0x004.
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/sts
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the ETB status register. The value
is read directly from HW register STS, 0x00C.
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/rrp
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the ETB RAM Read Pointer register
that is used to read entries from the Trace RAM over the APB
interface. The value is read directly from HW register RRP,
0x014.
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/rwp
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the ETB RAM Write Pointer register
that is used to sets the write pointer to write entries from
the CoreSight bus into the Trace RAM. The value is read directly
from HW register RWP, 0x018.
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/trg
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Similar to "trigger_cntr" above except that this value is
read directly from HW register TRG, 0x01C.
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/ctl
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the ETB Control register. The value
is read directly from HW register CTL, 0x020.
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/ffsr
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the ETB Formatter and Flush Status
register. The value is read directly from HW register FFSR,
0x300.
What: /sys/bus/coresight/devices/<memory_map>.etb/mgmt/ffcr
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the ETB Formatter and Flush Control
register. The value is read directly from HW register FFCR,
0x304.

View File

@ -359,6 +359,19 @@ Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Print the content of the Peripheral ID3 Register
(0xFEC). The value is taken directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trcconfig
Date: February 2016
KernelVersion: 4.07
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Print the content of the trace configuration register
(0x010) as currently set by SW.
What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt/trctraceid
Date: February 2016
KernelVersion: 4.07
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Print the content of the trace ID register (0x040).
What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr/trcidr0
Date: April 2015
KernelVersion: 4.01

View File

@ -0,0 +1,53 @@
What: /sys/bus/coresight/devices/<memory_map>.stm/enable_source
Date: April 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Enable/disable tracing on this specific trace macrocell.
Enabling the trace macrocell implies it has been configured
properly and a sink has been identified for it. The path
of coresight components linking the source to the sink is
configured and managed automatically by the coresight framework.
What: /sys/bus/coresight/devices/<memory_map>.stm/hwevent_enable
Date: April 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Provides access to the HW event enable register, used in
conjunction with HW event bank select register.
What: /sys/bus/coresight/devices/<memory_map>.stm/hwevent_select
Date: April 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Gives access to the HW event block select register
(STMHEBSR) in order to configure up to 256 channels. Used in
conjunction with "hwevent_enable" register as described above.
What: /sys/bus/coresight/devices/<memory_map>.stm/port_enable
Date: April 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Provides access to the stimulus port enable register
(STMSPER). Used in conjunction with "port_select" described
below.
What: /sys/bus/coresight/devices/<memory_map>.stm/port_select
Date: April 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Used to determine which bank of stimulus port bit in
register STMSPER (see above) apply to.
What: /sys/bus/coresight/devices/<memory_map>.stm/status
Date: April 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) List various control and status registers. The specific
layout and content is driver specific.
What: /sys/bus/coresight/devices/<memory_map>.stm/traceid
Date: April 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Holds the trace ID that will appear in the trace stream
coming from this trace entity.

View File

@ -6,3 +6,80 @@ Description: (RW) Disables write access to the Trace RAM by stopping the
formatter after a defined number of words have been stored
following the trigger event. Additional interface for this
driver are expected to be added as it matures.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/rsz
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Defines the size, in 32-bit words, of the local RAM buffer.
The value is read directly from HW register RSZ, 0x004.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/sts
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the TMC status register. The value
is read directly from HW register STS, 0x00C.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/rrp
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the TMC RAM Read Pointer register
that is used to read entries from the Trace RAM over the APB
interface. The value is read directly from HW register RRP,
0x014.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/rwp
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the TMC RAM Write Pointer register
that is used to sets the write pointer to write entries from
the CoreSight bus into the Trace RAM. The value is read directly
from HW register RWP, 0x018.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/trg
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Similar to "trigger_cntr" above except that this value is
read directly from HW register TRG, 0x01C.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/ctl
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the TMC Control register. The value
is read directly from HW register CTL, 0x020.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/ffsr
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the TMC Formatter and Flush Status
register. The value is read directly from HW register FFSR,
0x300.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/ffcr
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the TMC Formatter and Flush Control
register. The value is read directly from HW register FFCR,
0x304.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/mode
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Shows the value held by the TMC Mode register, which
indicate the mode the device has been configured to enact. The
The value is read directly from the MODE register, 0x028.
What: /sys/bus/coresight/devices/<memory_map>.tmc/mgmt/devid
Date: March 2016
KernelVersion: 4.7
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) Indicates the capabilities of the Coresight TMC.
The value is read directly from the DEVID register, 0xFC8,

View File

@ -0,0 +1,29 @@
What: /sys/bus/mcb/devices/mcb:X
Date: March 2016
KernelVersion: 4.7
Contact: Johannes Thumshirn <jth@kernel.org>
Description: Hardware chip or device hosting the MEN chameleon bus
What: /sys/bus/mcb/devices/mcb:X/revision
Date: March 2016
KernelVersion: 4.7
Contact: Johannes Thumshirn <jth@kernel.org>
Description: The FPGA's revision number
What: /sys/bus/mcb/devices/mcb:X/minor
Date: March 2016
KernelVersion: 4.7
Contact: Johannes Thumshirn <jth@kernel.org>
Description: The FPGA's minor number
What: /sys/bus/mcb/devices/mcb:X/model
Date: March 2016
KernelVersion: 4.7
Contact: Johannes Thumshirn <jth@kernel.org>
Description: The FPGA's model number
What: /sys/bus/mcb/devices/mcb:X/name
Date: March 2016
KernelVersion: 4.7
Contact: Johannes Thumshirn <jth@kernel.org>
Description: The FPGA's name

View File

@ -12,3 +12,13 @@ KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description:
Shows the number of channels per master on this STM device.
What: /sys/class/stm/<stm>/hw_override
Date: March 2016
KernelVersion: 4.7
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description:
Reads as 0 if master numbers in the STP stream produced by
this stm device will match the master numbers assigned by
the software or 1 if the stm hardware overrides software
assigned masters.

View File

@ -19,6 +19,7 @@ its hardware characteristcs.
- "arm,coresight-etm3x", "arm,primecell";
- "arm,coresight-etm4x", "arm,primecell";
- "qcom,coresight-replicator1x", "arm,primecell";
- "arm,coresight-stm", "arm,primecell"; [1]
* reg: physical base address and length of the register
set(s) of the component.
@ -36,6 +37,14 @@ its hardware characteristcs.
layout using the generic DT graph presentation found in
"bindings/graph.txt".
* Additional required properties for System Trace Macrocells (STM):
* reg: along with the physical base address and length of the register
set as described above, another entry is required to describe the
mapping of the extended stimulus port area.
* reg-names: the only acceptable values are "stm-base" and
"stm-stimulus-base", each corresponding to the areas defined in "reg".
* Required properties for devices that don't show up on the AMBA bus, such as
non-configurable replicators:
@ -202,3 +211,22 @@ Example:
};
};
};
4. STM
stm@20100000 {
compatible = "arm,coresight-stm", "arm,primecell";
reg = <0 0x20100000 0 0x1000>,
<0 0x28000000 0 0x180000>;
reg-names = "stm-base", "stm-stimulus-base";
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
port {
stm_out_port: endpoint {
remote-endpoint = <&main_funnel_in_port2>;
};
};
};
[1]. There is currently two version of STM: STM32 and STM500. Both
have the same HW interface and as such don't need an explicit binding name.

View File

@ -190,8 +190,8 @@ expected to be accessed and controlled using those entries.
Last but not least, "struct module *owner" is expected to be set to reflect
the information carried in "THIS_MODULE".
How to use
----------
How to use the tracer modules
-----------------------------
Before trace collection can start, a coresight sink needs to be identify.
There is no limit on the amount of sinks (nor sources) that can be enabled at
@ -297,3 +297,36 @@ Info Tracing enabled
Instruction 13570831 0x8026B584 E28DD00C false ADD sp,sp,#0xc
Instruction 0 0x8026B588 E8BD8000 true LDM sp!,{pc}
Timestamp Timestamp: 17107041535
How to use the STM module
-------------------------
Using the System Trace Macrocell module is the same as the tracers - the only
difference is that clients are driving the trace capture rather
than the program flow through the code.
As with any other CoreSight component, specifics about the STM tracer can be
found in sysfs with more information on each entry being found in [1]:
root@genericarmv8:~# ls /sys/bus/coresight/devices/20100000.stm
enable_source hwevent_select port_enable subsystem uevent
hwevent_enable mgmt port_select traceid
root@genericarmv8:~#
Like any other source a sink needs to be identified and the STM enabled before
being used:
root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/20010000.etf/enable_sink
root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/20100000.stm/enable_source
From there user space applications can request and use channels using the devfs
interface provided for that purpose by the generic STM API:
root@genericarmv8:~# ls -l /dev/20100000.stm
crw------- 1 root root 10, 61 Jan 3 18:11 /dev/20100000.stm
root@genericarmv8:~#
Details on how to use the generic STM API can be found here [2].
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm
[2]. Documentation/trace/stm.txt

View File

@ -33,7 +33,15 @@ temperature conversion at a time. If none of the devices are parasite
powered it would be possible to convert all the devices at the same
time and then go back to read individual sensors. That isn't
currently supported. The driver also doesn't support reduced
precision (which would also reduce the conversion time).
precision (which would also reduce the conversion time) when reading values.
Writing a value between 9 and 12 to the sysfs w1_slave file will change the
precision of the sensor for the next readings. This value is in (volatile)
SRAM, so it is reset when the sensor gets power-cycled.
To store the current precision configuration into EEPROM, the value 0
has to be written to the sysfs w1_slave file. Since the EEPROM has a limited
amount of writes (>50k), this command should be used wisely.
The module parameter strong_pullup can be set to 0 to disable the
strong pullup, 1 to enable autodetection or 2 to force strong pullup.

View File

@ -9843,6 +9843,7 @@ F: drivers/mmc/host/dw_mmc*
SYSTEM TRACE MODULE CLASS
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm.git
F: Documentation/trace/stm.txt
F: drivers/hwtracing/stm/
F: include/linux/stm.h

View File

@ -279,8 +279,7 @@ if RTC_LIB=n
config RTC
tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \
&& !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML
depends on ALPHA || (MIPS && MACH_LOONGSON64) || MN10300
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@ -585,7 +584,6 @@ config TELCLOCK
config DEVPORT
bool
depends on !M68K
depends on ISA || PCI
default y

View File

@ -81,7 +81,6 @@ static int xilly_map_single_of(struct xilly_endpoint *ep,
{
dma_addr_t addr;
struct xilly_mapping *this;
int rc;
this = kzalloc(sizeof(*this), GFP_KERNEL);
if (!this)
@ -101,15 +100,7 @@ static int xilly_map_single_of(struct xilly_endpoint *ep,
*ret_dma_handle = addr;
rc = devm_add_action(ep->dev, xilly_of_unmap, this);
if (rc) {
dma_unmap_single(ep->dev, addr, size, direction);
kfree(this);
return rc;
}
return 0;
return devm_add_action_or_reset(ep->dev, xilly_of_unmap, this);
}
static struct xilly_endpoint_hardware of_hw = {

View File

@ -98,7 +98,6 @@ static int xilly_map_single_pci(struct xilly_endpoint *ep,
int pci_direction;
dma_addr_t addr;
struct xilly_mapping *this;
int rc;
this = kzalloc(sizeof(*this), GFP_KERNEL);
if (!this)
@ -120,14 +119,7 @@ static int xilly_map_single_pci(struct xilly_endpoint *ep,
*ret_dma_handle = addr;
rc = devm_add_action(ep->dev, xilly_pci_unmap, this);
if (rc) {
pci_unmap_single(ep->pdev, addr, size, pci_direction);
kfree(this);
return rc;
}
return 0;
return devm_add_action_or_reset(ep->dev, xilly_pci_unmap, this);
}
static struct xilly_endpoint_hardware pci_hw = {

View File

@ -597,27 +597,55 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
static void vmbus_wait_for_unload(void)
{
int cpu = smp_processor_id();
void *page_addr = hv_context.synic_message_page[cpu];
struct hv_message *msg = (struct hv_message *)page_addr +
VMBUS_MESSAGE_SINT;
int cpu;
void *page_addr;
struct hv_message *msg;
struct vmbus_channel_message_header *hdr;
bool unloaded = false;
u32 message_type;
/*
* CHANNELMSG_UNLOAD_RESPONSE is always delivered to the CPU which was
* used for initial contact or to CPU0 depending on host version. When
* we're crashing on a different CPU let's hope that IRQ handler on
* the cpu which receives CHANNELMSG_UNLOAD_RESPONSE is still
* functional and vmbus_unload_response() will complete
* vmbus_connection.unload_event. If not, the last thing we can do is
* read message pages for all CPUs directly.
*/
while (1) {
if (READ_ONCE(msg->header.message_type) == HVMSG_NONE) {
mdelay(10);
continue;
if (completion_done(&vmbus_connection.unload_event))
break;
for_each_online_cpu(cpu) {
page_addr = hv_context.synic_message_page[cpu];
msg = (struct hv_message *)page_addr +
VMBUS_MESSAGE_SINT;
message_type = READ_ONCE(msg->header.message_type);
if (message_type == HVMSG_NONE)
continue;
hdr = (struct vmbus_channel_message_header *)
msg->u.payload;
if (hdr->msgtype == CHANNELMSG_UNLOAD_RESPONSE)
complete(&vmbus_connection.unload_event);
vmbus_signal_eom(msg, message_type);
}
hdr = (struct vmbus_channel_message_header *)msg->u.payload;
if (hdr->msgtype == CHANNELMSG_UNLOAD_RESPONSE)
unloaded = true;
mdelay(10);
}
vmbus_signal_eom(msg);
if (unloaded)
break;
/*
* We're crashing and already got the UNLOAD_RESPONSE, cleanup all
* maybe-pending messages on all CPUs to be able to receive new
* messages after we reconnect.
*/
for_each_online_cpu(cpu) {
page_addr = hv_context.synic_message_page[cpu];
msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
msg->header.message_type = HVMSG_NONE;
}
}

View File

@ -495,3 +495,4 @@ void vmbus_set_event(struct vmbus_channel *channel)
hv_do_hypercall(HVCALL_SIGNAL_EVENT, channel->sig_event, NULL);
}
EXPORT_SYMBOL_GPL(vmbus_set_event);

View File

@ -714,7 +714,7 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
*/
if ((start_pfn >= has->end_pfn))
if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
/*
* If the current hot add-request extends beyond
@ -768,7 +768,7 @@ static unsigned long handle_pg_range(unsigned long pg_start,
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
*/
if ((start_pfn >= has->end_pfn))
if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
old_covered_state = has->covered_end_pfn;
@ -1400,6 +1400,7 @@ static void balloon_onchannelcallback(void *context)
* This is a normal hot-add request specifying
* hot-add memory.
*/
dm->host_specified_ha_region = false;
ha_pg_range = &ha_msg->range;
dm->ha_wrk.ha_page_range = *ha_pg_range;
dm->ha_wrk.ha_region_range.page_range = 0;

View File

@ -78,9 +78,11 @@ static void kvp_send_key(struct work_struct *dummy);
static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
static void kvp_timeout_func(struct work_struct *dummy);
static void kvp_host_handshake_func(struct work_struct *dummy);
static void kvp_register(int);
static DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func);
static DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func);
static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
static const char kvp_devname[] = "vmbus/hv_kvp";
@ -130,6 +132,11 @@ static void kvp_timeout_func(struct work_struct *dummy)
hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
}
static void kvp_host_handshake_func(struct work_struct *dummy)
{
hv_poll_channel(kvp_transaction.recv_channel, hv_kvp_onchannelcallback);
}
static int kvp_handle_handshake(struct hv_kvp_msg *msg)
{
switch (msg->kvp_hdr.operation) {
@ -154,6 +161,12 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
pr_debug("KVP: userspace daemon ver. %d registered\n",
KVP_OP_REGISTER);
kvp_register(dm_reg_value);
/*
* If we're still negotiating with the host cancel the timeout
* work to not poll the channel twice.
*/
cancel_delayed_work_sync(&kvp_host_handshake_work);
hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
return 0;
@ -594,7 +607,22 @@ void hv_kvp_onchannelcallback(void *context)
struct icmsg_negotiate *negop = NULL;
int util_fw_version;
int kvp_srv_version;
static enum {NEGO_NOT_STARTED,
NEGO_IN_PROGRESS,
NEGO_FINISHED} host_negotiatied = NEGO_NOT_STARTED;
if (host_negotiatied == NEGO_NOT_STARTED &&
kvp_transaction.state < HVUTIL_READY) {
/*
* If userspace daemon is not connected and host is asking
* us to negotiate we need to delay to not lose messages.
* This is important for Failover IP setting.
*/
host_negotiatied = NEGO_IN_PROGRESS;
schedule_delayed_work(&kvp_host_handshake_work,
HV_UTIL_NEGO_TIMEOUT * HZ);
return;
}
if (kvp_transaction.state > HVUTIL_READY)
return;
@ -672,6 +700,8 @@ void hv_kvp_onchannelcallback(void *context)
vmbus_sendpacket(channel, recv_buffer,
recvlen, requestid,
VM_PKT_DATA_INBAND, 0);
host_negotiatied = NEGO_FINISHED;
}
}
@ -708,6 +738,7 @@ hv_kvp_init(struct hv_util_service *srv)
void hv_kvp_deinit(void)
{
kvp_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&kvp_host_handshake_work);
cancel_delayed_work_sync(&kvp_timeout_work);
cancel_work_sync(&kvp_sendkey_work);
hvutil_transport_destroy(hvt);

View File

@ -35,6 +35,11 @@
*/
#define HV_UTIL_TIMEOUT 30
/*
* Timeout for guest-host handshake for services.
*/
#define HV_UTIL_NEGO_TIMEOUT 60
/*
* The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
* is set by CPUID(HVCPUID_VERSION_FEATURES).
@ -620,9 +625,21 @@ extern struct vmbus_channel_message_table_entry
channel_message_table[CHANNELMSG_COUNT];
/* Free the message slot and signal end-of-message if required */
static inline void vmbus_signal_eom(struct hv_message *msg)
static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
{
msg->header.message_type = HVMSG_NONE;
/*
* On crash we're reading some other CPU's message page and we need
* to be careful: this other CPU may already had cleared the header
* and the host may already had delivered some other message there.
* In case we blindly write msg->header.message_type we're going
* to lose it. We can still lose a message of the same type but
* we count on the fact that there can only be one
* CHANNELMSG_UNLOAD_RESPONSE and we don't care about other messages
* on crash.
*/
if (cmpxchg(&msg->header.message_type, old_msg_type,
HVMSG_NONE) != old_msg_type)
return;
/*
* Make sure the write to MessageType (ie set to
@ -667,8 +684,6 @@ void vmbus_disconnect(void);
int vmbus_post_msg(void *buffer, size_t buflen);
void vmbus_set_event(struct vmbus_channel *channel);
void vmbus_on_event(unsigned long data);
void vmbus_on_msg_dpc(unsigned long data);

View File

@ -33,25 +33,21 @@
void hv_begin_read(struct hv_ring_buffer_info *rbi)
{
rbi->ring_buffer->interrupt_mask = 1;
mb();
virt_mb();
}
u32 hv_end_read(struct hv_ring_buffer_info *rbi)
{
u32 read;
u32 write;
rbi->ring_buffer->interrupt_mask = 0;
mb();
virt_mb();
/*
* Now check to see if the ring buffer is still empty.
* If it is not, we raced and we need to process new
* incoming messages.
*/
hv_get_ringbuffer_availbytes(rbi, &read, &write);
return read;
return hv_get_bytes_to_read(rbi);
}
/*
@ -72,69 +68,17 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
{
mb();
if (rbi->ring_buffer->interrupt_mask)
virt_mb();
if (READ_ONCE(rbi->ring_buffer->interrupt_mask))
return false;
/* check interrupt_mask before read_index */
rmb();
virt_rmb();
/*
* This is the only case we need to signal when the
* ring transitions from being empty to non-empty.
*/
if (old_write == rbi->ring_buffer->read_index)
return true;
return false;
}
/*
* To optimize the flow management on the send-side,
* when the sender is blocked because of lack of
* sufficient space in the ring buffer, potential the
* consumer of the ring buffer can signal the producer.
* This is controlled by the following parameters:
*
* 1. pending_send_sz: This is the size in bytes that the
* producer is trying to send.
* 2. The feature bit feat_pending_send_sz set to indicate if
* the consumer of the ring will signal when the ring
* state transitions from being full to a state where
* there is room for the producer to send the pending packet.
*/
static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)
{
u32 cur_write_sz;
u32 r_size;
u32 write_loc;
u32 read_loc = rbi->ring_buffer->read_index;
u32 pending_sz;
/*
* Issue a full memory barrier before making the signaling decision.
* Here is the reason for having this barrier:
* If the reading of the pend_sz (in this function)
* were to be reordered and read before we commit the new read
* index (in the calling function) we could
* have a problem. If the host were to set the pending_sz after we
* have sampled pending_sz and go to sleep before we commit the
* read index, we could miss sending the interrupt. Issue a full
* memory barrier to address this.
*/
mb();
pending_sz = rbi->ring_buffer->pending_send_sz;
write_loc = rbi->ring_buffer->write_index;
/* If the other end is not blocked on write don't bother. */
if (pending_sz == 0)
return false;
r_size = rbi->ring_datasize;
cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) :
read_loc - write_loc;
if (cur_write_sz >= pending_sz)
if (old_write == READ_ONCE(rbi->ring_buffer->read_index))
return true;
return false;
@ -188,17 +132,9 @@ hv_set_next_read_location(struct hv_ring_buffer_info *ring_info,
u32 next_read_location)
{
ring_info->ring_buffer->read_index = next_read_location;
ring_info->priv_read_index = next_read_location;
}
/* Get the start of the ring buffer. */
static inline void *
hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info)
{
return (void *)ring_info->ring_buffer->buffer;
}
/* Get the size of the ring buffer. */
static inline u32
hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info)
@ -332,7 +268,6 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
{
int i = 0;
u32 bytes_avail_towrite;
u32 bytes_avail_toread;
u32 totalbytes_towrite = 0;
u32 next_write_location;
@ -348,9 +283,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
if (lock)
spin_lock_irqsave(&outring_info->ring_lock, flags);
hv_get_ringbuffer_availbytes(outring_info,
&bytes_avail_toread,
&bytes_avail_towrite);
bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
/*
* If there is only room for the packet, assume it is full.
@ -384,7 +317,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
sizeof(u64));
/* Issue a full memory barrier before updating the write index */
mb();
virt_mb();
/* Now, update the write location */
hv_set_next_write_location(outring_info, next_write_location);
@ -401,7 +334,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
void *buffer, u32 buflen, u32 *buffer_actual_len,
u64 *requestid, bool *signal, bool raw)
{
u32 bytes_avail_towrite;
u32 bytes_avail_toread;
u32 next_read_location = 0;
u64 prev_indices = 0;
@ -417,10 +349,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
*buffer_actual_len = 0;
*requestid = 0;
hv_get_ringbuffer_availbytes(inring_info,
&bytes_avail_toread,
&bytes_avail_towrite);
bytes_avail_toread = hv_get_bytes_to_read(inring_info);
/* Make sure there is something to read */
if (bytes_avail_toread < sizeof(desc)) {
/*
@ -464,7 +393,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
* the writer may start writing to the read area once the read index
* is updated.
*/
mb();
virt_mb();
/* Update the read index */
hv_set_next_read_location(inring_info, next_read_location);

View File

@ -41,6 +41,7 @@
#include <linux/ptrace.h>
#include <linux/screen_info.h>
#include <linux/kdebug.h>
#include <linux/efi.h>
#include "hyperv_vmbus.h"
static struct acpi_device *hv_acpi_dev;
@ -101,7 +102,10 @@ static struct notifier_block hyperv_panic_block = {
.notifier_call = hyperv_panic_event,
};
static const char *fb_mmio_name = "fb_range";
static struct resource *fb_mmio;
struct resource *hyperv_mmio;
DEFINE_SEMAPHORE(hyperv_mmio_lock);
static int vmbus_exists(void)
{
@ -708,7 +712,7 @@ static void hv_process_timer_expiration(struct hv_message *msg, int cpu)
if (dev->event_handler)
dev->event_handler(dev);
vmbus_signal_eom(msg);
vmbus_signal_eom(msg, HVMSG_TIMER_EXPIRED);
}
void vmbus_on_msg_dpc(unsigned long data)
@ -720,8 +724,9 @@ void vmbus_on_msg_dpc(unsigned long data)
struct vmbus_channel_message_header *hdr;
struct vmbus_channel_message_table_entry *entry;
struct onmessage_work_context *ctx;
u32 message_type = msg->header.message_type;
if (msg->header.message_type == HVMSG_NONE)
if (message_type == HVMSG_NONE)
/* no msg */
return;
@ -746,7 +751,7 @@ void vmbus_on_msg_dpc(unsigned long data)
entry->message_handler(hdr);
msg_handled:
vmbus_signal_eom(msg);
vmbus_signal_eom(msg, message_type);
}
static void vmbus_isr(void)
@ -1048,7 +1053,6 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
new_res->end = end;
/*
* Stick ranges from higher in address space at the front of the list.
* If two ranges are adjacent, merge them.
*/
do {
@ -1069,7 +1073,7 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
break;
}
if ((*old_res)->end < new_res->start) {
if ((*old_res)->start > new_res->end) {
new_res->sibling = *old_res;
if (prev_res)
(*prev_res)->sibling = new_res;
@ -1091,6 +1095,12 @@ static int vmbus_acpi_remove(struct acpi_device *device)
struct resource *next_res;
if (hyperv_mmio) {
if (fb_mmio) {
__release_region(hyperv_mmio, fb_mmio->start,
resource_size(fb_mmio));
fb_mmio = NULL;
}
for (cur_res = hyperv_mmio; cur_res; cur_res = next_res) {
next_res = cur_res->sibling;
kfree(cur_res);
@ -1100,6 +1110,30 @@ static int vmbus_acpi_remove(struct acpi_device *device)
return 0;
}
static void vmbus_reserve_fb(void)
{
int size;
/*
* Make a claim for the frame buffer in the resource tree under the
* first node, which will be the one below 4GB. The length seems to
* be underreported, particularly in a Generation 1 VM. So start out
* reserving a larger area and make it smaller until it succeeds.
*/
if (screen_info.lfb_base) {
if (efi_enabled(EFI_BOOT))
size = max_t(__u32, screen_info.lfb_size, 0x800000);
else
size = max_t(__u32, screen_info.lfb_size, 0x4000000);
for (; !fb_mmio && (size >= 0x100000); size >>= 1) {
fb_mmio = __request_region(hyperv_mmio,
screen_info.lfb_base, size,
fb_mmio_name, 0);
}
}
}
/**
* vmbus_allocate_mmio() - Pick a memory-mapped I/O range.
* @new: If successful, supplied a pointer to the
@ -1128,11 +1162,33 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
resource_size_t size, resource_size_t align,
bool fb_overlap_ok)
{
struct resource *iter;
resource_size_t range_min, range_max, start, local_min, local_max;
struct resource *iter, *shadow;
resource_size_t range_min, range_max, start;
const char *dev_n = dev_name(&device_obj->device);
u32 fb_end = screen_info.lfb_base + (screen_info.lfb_size << 1);
int i;
int retval;
retval = -ENXIO;
down(&hyperv_mmio_lock);
/*
* If overlaps with frame buffers are allowed, then first attempt to
* make the allocation from within the reserved region. Because it
* is already reserved, no shadow allocation is necessary.
*/
if (fb_overlap_ok && fb_mmio && !(min > fb_mmio->end) &&
!(max < fb_mmio->start)) {
range_min = fb_mmio->start;
range_max = fb_mmio->end;
start = (range_min + align - 1) & ~(align - 1);
for (; start + size - 1 <= range_max; start += align) {
*new = request_mem_region_exclusive(start, size, dev_n);
if (*new) {
retval = 0;
goto exit;
}
}
}
for (iter = hyperv_mmio; iter; iter = iter->sibling) {
if ((iter->start >= max) || (iter->end <= min))
@ -1140,45 +1196,55 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
range_min = iter->start;
range_max = iter->end;
start = (range_min + align - 1) & ~(align - 1);
for (; start + size - 1 <= range_max; start += align) {
shadow = __request_region(iter, start, size, NULL,
IORESOURCE_BUSY);
if (!shadow)
continue;
/* If this range overlaps the frame buffer, split it into
two tries. */
for (i = 0; i < 2; i++) {
local_min = range_min;
local_max = range_max;
if (fb_overlap_ok || (range_min >= fb_end) ||
(range_max <= screen_info.lfb_base)) {
i++;
} else {
if ((range_min <= screen_info.lfb_base) &&
(range_max >= screen_info.lfb_base)) {
/*
* The frame buffer is in this window,
* so trim this into the part that
* preceeds the frame buffer.
*/
local_max = screen_info.lfb_base - 1;
range_min = fb_end;
} else {
range_min = fb_end;
continue;
}
*new = request_mem_region_exclusive(start, size, dev_n);
if (*new) {
shadow->name = (char *)*new;
retval = 0;
goto exit;
}
start = (local_min + align - 1) & ~(align - 1);
for (; start + size - 1 <= local_max; start += align) {
*new = request_mem_region_exclusive(start, size,
dev_n);
if (*new)
return 0;
}
__release_region(iter, start, size);
}
}
return -ENXIO;
exit:
up(&hyperv_mmio_lock);
return retval;
}
EXPORT_SYMBOL_GPL(vmbus_allocate_mmio);
/**
* vmbus_free_mmio() - Free a memory-mapped I/O range.
* @start: Base address of region to release.
* @size: Size of the range to be allocated
*
* This function releases anything requested by
* vmbus_mmio_allocate().
*/
void vmbus_free_mmio(resource_size_t start, resource_size_t size)
{
struct resource *iter;
down(&hyperv_mmio_lock);
for (iter = hyperv_mmio; iter; iter = iter->sibling) {
if ((iter->start >= start + size) || (iter->end <= start))
continue;
__release_region(iter, start, size);
}
release_mem_region(start, size);
up(&hyperv_mmio_lock);
}
EXPORT_SYMBOL_GPL(vmbus_free_mmio);
/**
* vmbus_cpu_number_to_vp_number() - Map CPU to VP.
* @cpu_number: CPU number in Linux terms
@ -1219,8 +1285,10 @@ static int vmbus_acpi_add(struct acpi_device *device)
if (ACPI_FAILURE(result))
continue;
if (hyperv_mmio)
if (hyperv_mmio) {
vmbus_reserve_fb();
break;
}
}
ret_val = 0;

View File

@ -78,4 +78,15 @@ config CORESIGHT_QCOM_REPLICATOR
programmable ATB replicator sends the ATB trace stream from the
ETB/ETF to the TPIUi and ETR.
config CORESIGHT_STM
bool "CoreSight System Trace Macrocell driver"
depends on (ARM && !(CPU_32v3 || CPU_32v4 || CPU_32v4T)) || ARM64
select CORESIGHT_LINKS_AND_SINKS
select STM
help
This driver provides support for hardware assisted software
instrumentation based tracing. This is primarily used for
logging useful software events or data coming from various entities
in the system, possibly running different OSs
endif

View File

@ -1,15 +1,18 @@
#
# Makefile for CoreSight drivers.
#
obj-$(CONFIG_CORESIGHT) += coresight.o
obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o
obj-$(CONFIG_OF) += of_coresight.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \
coresight-tmc-etf.o \
coresight-tmc-etr.o
obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
coresight-replicator.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
coresight-etm3x-sysfs.o \
coresight-etm-perf.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
coresight-etm3x-sysfs.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o

View File

@ -70,26 +70,6 @@
#define ETB_FFSR_BIT 1
#define ETB_FRAME_SIZE_WORDS 4
/**
* struct cs_buffer - keep track of a recording session' specifics
* @cur: index of the current buffer
* @nr_pages: max number of pages granted to us
* @offset: offset within the current buffer
* @data_size: how much we collected in this run
* @lost: other than zero if we had a HW buffer wrap around
* @snapshot: is this run in snapshot mode
* @data_pages: a handle the ring buffer
*/
struct cs_buffers {
unsigned int cur;
unsigned int nr_pages;
unsigned long offset;
local_t data_size;
local_t lost;
bool snapshot;
void **data_pages;
};
/**
* struct etb_drvdata - specifics associated to an ETB component
* @base: memory mapped base address for this component.
@ -440,7 +420,7 @@ static void etb_update_buffer(struct coresight_device *csdev,
u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1);
/* The new read pointer must be frame size aligned */
to_read -= handle->size & mask;
to_read = handle->size & mask;
/*
* Move the RAM read pointer up, keeping in mind that
* everything is in frame size units.
@ -448,7 +428,8 @@ static void etb_update_buffer(struct coresight_device *csdev,
read_ptr = (write_ptr + drvdata->buffer_depth) -
to_read / ETB_FRAME_SIZE_WORDS;
/* Wrap around if need be*/
read_ptr &= ~(drvdata->buffer_depth - 1);
if (read_ptr > (drvdata->buffer_depth - 1))
read_ptr -= drvdata->buffer_depth;
/* let the decoder know we've skipped ahead */
local_inc(&buf->lost);
}
@ -579,47 +560,29 @@ static const struct file_operations etb_fops = {
.llseek = no_llseek,
};
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long flags;
u32 etb_rdr, etb_sr, etb_rrp, etb_rwp;
u32 etb_trg, etb_cr, etb_ffsr, etb_ffcr;
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
#define coresight_etb10_simple_func(name, offset) \
coresight_simple_func(struct etb_drvdata, name, offset)
pm_runtime_get_sync(drvdata->dev);
spin_lock_irqsave(&drvdata->spinlock, flags);
CS_UNLOCK(drvdata->base);
coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG);
coresight_etb10_simple_func(sts, ETB_STATUS_REG);
coresight_etb10_simple_func(rrp, ETB_RAM_READ_POINTER);
coresight_etb10_simple_func(rwp, ETB_RAM_WRITE_POINTER);
coresight_etb10_simple_func(trg, ETB_TRG);
coresight_etb10_simple_func(ctl, ETB_CTL_REG);
coresight_etb10_simple_func(ffsr, ETB_FFSR);
coresight_etb10_simple_func(ffcr, ETB_FFCR);
etb_rdr = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG);
etb_sr = readl_relaxed(drvdata->base + ETB_STATUS_REG);
etb_rrp = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
etb_rwp = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
etb_trg = readl_relaxed(drvdata->base + ETB_TRG);
etb_cr = readl_relaxed(drvdata->base + ETB_CTL_REG);
etb_ffsr = readl_relaxed(drvdata->base + ETB_FFSR);
etb_ffcr = readl_relaxed(drvdata->base + ETB_FFCR);
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
pm_runtime_put(drvdata->dev);
return sprintf(buf,
"Depth:\t\t0x%x\n"
"Status:\t\t0x%x\n"
"RAM read ptr:\t0x%x\n"
"RAM wrt ptr:\t0x%x\n"
"Trigger cnt:\t0x%x\n"
"Control:\t0x%x\n"
"Flush status:\t0x%x\n"
"Flush ctrl:\t0x%x\n",
etb_rdr, etb_sr, etb_rrp, etb_rwp,
etb_trg, etb_cr, etb_ffsr, etb_ffcr);
return -EINVAL;
}
static DEVICE_ATTR_RO(status);
static struct attribute *coresight_etb_mgmt_attrs[] = {
&dev_attr_rdp.attr,
&dev_attr_sts.attr,
&dev_attr_rrp.attr,
&dev_attr_rwp.attr,
&dev_attr_trg.attr,
&dev_attr_ctl.attr,
&dev_attr_ffsr.attr,
&dev_attr_ffcr.attr,
NULL,
};
static ssize_t trigger_cntr_show(struct device *dev,
struct device_attribute *attr, char *buf)
@ -649,10 +612,23 @@ static DEVICE_ATTR_RW(trigger_cntr);
static struct attribute *coresight_etb_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etb);
static const struct attribute_group coresight_etb_group = {
.attrs = coresight_etb_attrs,
};
static const struct attribute_group coresight_etb_mgmt_group = {
.attrs = coresight_etb_mgmt_attrs,
.name = "mgmt",
};
const struct attribute_group *coresight_etb_groups[] = {
&coresight_etb_group,
&coresight_etb_mgmt_group,
NULL,
};
static int etb_probe(struct amba_device *adev, const struct amba_id *id)
{
@ -729,7 +705,6 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
if (ret)
goto err_misc_register;
dev_info(dev, "ETB initialized\n");
return 0;
err_misc_register:

View File

@ -1221,26 +1221,19 @@ static struct attribute *coresight_etm_attrs[] = {
NULL,
};
#define coresight_simple_func(name, offset) \
static ssize_t name##_show(struct device *_dev, \
struct device_attribute *attr, char *buf) \
{ \
struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \
return scnprintf(buf, PAGE_SIZE, "0x%x\n", \
readl_relaxed(drvdata->base + offset)); \
} \
DEVICE_ATTR_RO(name)
#define coresight_etm3x_simple_func(name, offset) \
coresight_simple_func(struct etm_drvdata, name, offset)
coresight_simple_func(etmccr, ETMCCR);
coresight_simple_func(etmccer, ETMCCER);
coresight_simple_func(etmscr, ETMSCR);
coresight_simple_func(etmidr, ETMIDR);
coresight_simple_func(etmcr, ETMCR);
coresight_simple_func(etmtraceidr, ETMTRACEIDR);
coresight_simple_func(etmteevr, ETMTEEVR);
coresight_simple_func(etmtssvr, ETMTSSCR);
coresight_simple_func(etmtecr1, ETMTECR1);
coresight_simple_func(etmtecr2, ETMTECR2);
coresight_etm3x_simple_func(etmccr, ETMCCR);
coresight_etm3x_simple_func(etmccer, ETMCCER);
coresight_etm3x_simple_func(etmscr, ETMSCR);
coresight_etm3x_simple_func(etmidr, ETMIDR);
coresight_etm3x_simple_func(etmcr, ETMCR);
coresight_etm3x_simple_func(etmtraceidr, ETMTRACEIDR);
coresight_etm3x_simple_func(etmteevr, ETMTEEVR);
coresight_etm3x_simple_func(etmtssvr, ETMTSSCR);
coresight_etm3x_simple_func(etmtecr1, ETMTECR1);
coresight_etm3x_simple_func(etmtecr2, ETMTECR2);
static struct attribute *coresight_etm_mgmt_attrs[] = {
&dev_attr_etmccr.attr,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
#ifndef _CORESIGHT_CORESIGHT_ETM_H
#define _CORESIGHT_CORESIGHT_ETM_H
#include <asm/local.h>
#include <linux/spinlock.h>
#include "coresight-priv.h"
@ -175,71 +176,38 @@
#define ETM_MODE_TRACE_RESET BIT(25)
#define ETM_MODE_TRACE_ERR BIT(26)
#define ETM_MODE_VIEWINST_STARTSTOP BIT(27)
#define ETMv4_MODE_ALL 0xFFFFFFF
#define ETMv4_MODE_ALL (GENMASK(27, 0) | \
ETM_MODE_EXCL_KERN | \
ETM_MODE_EXCL_USER)
#define TRCSTATR_IDLE_BIT 0
#define ETM_DEFAULT_ADDR_COMP 0
/* secure state access levels */
#define ETM_EXLEVEL_S_APP BIT(8)
#define ETM_EXLEVEL_S_OS BIT(9)
#define ETM_EXLEVEL_S_NA BIT(10)
#define ETM_EXLEVEL_S_HYP BIT(11)
/* non-secure state access levels */
#define ETM_EXLEVEL_NS_APP BIT(12)
#define ETM_EXLEVEL_NS_OS BIT(13)
#define ETM_EXLEVEL_NS_HYP BIT(14)
#define ETM_EXLEVEL_NS_NA BIT(15)
/**
* struct etm4_drvdata - specifics associated to an ETM component
* @base: Memory mapped base address for this component.
* @dev: The device entity associated to this component.
* @csdev: Component vitals needed by the framework.
* @spinlock: Only one at a time pls.
* @cpu: The cpu this component is affined to.
* @arch: ETM version number.
* @enable: Is this ETM currently tracing.
* @sticky_enable: true if ETM base configuration has been done.
* @boot_enable:True if we should start tracing at boot time.
* @os_unlock: True if access to management registers is allowed.
* @nr_pe: The number of processing entity available for tracing.
* @nr_pe_cmp: The number of processing entity comparator inputs that are
* available for tracing.
* @nr_addr_cmp:Number of pairs of address comparators available
* as found in ETMIDR4 0-3.
* @nr_cntr: Number of counters as found in ETMIDR5 bit 28-30.
* @nr_ext_inp: Number of external input.
* @numcidc: Number of contextID comparators.
* @numvmidc: Number of VMID comparators.
* @nrseqstate: The number of sequencer states that are implemented.
* @nr_event: Indicates how many events the trace unit support.
* @nr_resource:The number of resource selection pairs available for tracing.
* @nr_ss_cmp: Number of single-shot comparator controls that are available.
* struct etmv4_config - configuration information related to an ETMv4
* @mode: Controls various modes supported by this ETM.
* @trcid: value of the current ID for this component.
* @trcid_size: Indicates the trace ID width.
* @instrp0: Tracing of load and store instructions
* as P0 elements is supported.
* @trccond: If the trace unit supports conditional
* instruction tracing.
* @retstack: Indicates if the implementation supports a return stack.
* @trc_error: Whether a trace unit can trace a system
* error exception.
* @atbtrig: If the implementation can support ATB triggers
* @lpoverride: If the implementation can support low-power state over.
* @pe_sel: Controls which PE to trace.
* @cfg: Controls the tracing options.
* @eventctrl0: Controls the tracing of arbitrary events.
* @eventctrl1: Controls the behavior of the events that @event_ctrl0 selects.
* @stallctl: If functionality that prevents trace unit buffer overflows
* is available.
* @sysstall: Does the system support stall control of the PE?
* @nooverflow: Indicate if overflow prevention is supported.
* @stall_ctrl: Enables trace unit functionality that prevents trace
* unit buffer overflows.
* @ts_size: Global timestamp size field.
* @ts_ctrl: Controls the insertion of global timestamps in the
* trace streams.
* @syncpr: Indicates if an implementation has a fixed
* synchronization period.
* @syncfreq: Controls how often trace synchronization requests occur.
* @trccci: Indicates if the trace unit supports cycle counting
* for instruction.
* @ccsize: Indicates the size of the cycle counter in bits.
* @ccitmin: minimum value that can be programmed in
* the TRCCCCTLR register.
* @ccctlr: Sets the threshold value for cycle counting.
* @trcbb: Indicates if the trace unit supports branch broadcast tracing.
* @q_support: Q element support characteristics.
* @vinst_ctrl: Controls instruction trace filtering.
* @viiectlr: Set or read, the address range comparators.
* @vissctlr: Set, or read, the single address comparators that control the
@ -264,73 +232,28 @@
* @addr_acc: Address comparator access type.
* @addr_type: Current status of the comparator register.
* @ctxid_idx: Context ID index selector.
* @ctxid_size: Size of the context ID field to consider.
* @ctxid_pid: Value of the context ID comparator.
* @ctxid_vpid: Virtual PID seen by users if PID namespace is enabled, otherwise
* the same value of ctxid_pid.
* @ctxid_mask0:Context ID comparator mask for comparator 0-3.
* @ctxid_mask1:Context ID comparator mask for comparator 4-7.
* @vmid_idx: VM ID index selector.
* @vmid_size: Size of the VM ID comparator to consider.
* @vmid_val: Value of the VM ID comparator.
* @vmid_mask0: VM ID comparator mask for comparator 0-3.
* @vmid_mask1: VM ID comparator mask for comparator 4-7.
* @s_ex_level: In secure state, indicates whether instruction tracing is
* supported for the corresponding Exception level.
* @ns_ex_level:In non-secure state, indicates whether instruction tracing is
* supported for the corresponding Exception level.
* @ext_inp: External input selection.
*/
struct etmv4_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
spinlock_t spinlock;
int cpu;
u8 arch;
bool enable;
bool sticky_enable;
bool boot_enable;
bool os_unlock;
u8 nr_pe;
u8 nr_pe_cmp;
u8 nr_addr_cmp;
u8 nr_cntr;
u8 nr_ext_inp;
u8 numcidc;
u8 numvmidc;
u8 nrseqstate;
u8 nr_event;
u8 nr_resource;
u8 nr_ss_cmp;
struct etmv4_config {
u32 mode;
u8 trcid;
u8 trcid_size;
bool instrp0;
bool trccond;
bool retstack;
bool trc_error;
bool atbtrig;
bool lpoverride;
u32 pe_sel;
u32 cfg;
u32 eventctrl0;
u32 eventctrl1;
bool stallctl;
bool sysstall;
bool nooverflow;
u32 stall_ctrl;
u8 ts_size;
u32 ts_ctrl;
bool syncpr;
u32 syncfreq;
bool trccci;
u8 ccsize;
u8 ccitmin;
u32 ccctlr;
bool trcbb;
u32 bb_ctrl;
bool q_support;
u32 vinst_ctrl;
u32 viiectlr;
u32 vissctlr;
@ -353,19 +276,119 @@ struct etmv4_drvdata {
u64 addr_acc[ETM_MAX_SINGLE_ADDR_CMP];
u8 addr_type[ETM_MAX_SINGLE_ADDR_CMP];
u8 ctxid_idx;
u8 ctxid_size;
u64 ctxid_pid[ETMv4_MAX_CTXID_CMP];
u64 ctxid_vpid[ETMv4_MAX_CTXID_CMP];
u32 ctxid_mask0;
u32 ctxid_mask1;
u8 vmid_idx;
u8 vmid_size;
u64 vmid_val[ETM_MAX_VMID_CMP];
u32 vmid_mask0;
u32 vmid_mask1;
u32 ext_inp;
};
/**
* struct etm4_drvdata - specifics associated to an ETM component
* @base: Memory mapped base address for this component.
* @dev: The device entity associated to this component.
* @csdev: Component vitals needed by the framework.
* @spinlock: Only one at a time pls.
* @mode: This tracer's mode, i.e sysFS, Perf or disabled.
* @cpu: The cpu this component is affined to.
* @arch: ETM version number.
* @nr_pe: The number of processing entity available for tracing.
* @nr_pe_cmp: The number of processing entity comparator inputs that are
* available for tracing.
* @nr_addr_cmp:Number of pairs of address comparators available
* as found in ETMIDR4 0-3.
* @nr_cntr: Number of counters as found in ETMIDR5 bit 28-30.
* @nr_ext_inp: Number of external input.
* @numcidc: Number of contextID comparators.
* @numvmidc: Number of VMID comparators.
* @nrseqstate: The number of sequencer states that are implemented.
* @nr_event: Indicates how many events the trace unit support.
* @nr_resource:The number of resource selection pairs available for tracing.
* @nr_ss_cmp: Number of single-shot comparator controls that are available.
* @trcid: value of the current ID for this component.
* @trcid_size: Indicates the trace ID width.
* @ts_size: Global timestamp size field.
* @ctxid_size: Size of the context ID field to consider.
* @vmid_size: Size of the VM ID comparator to consider.
* @ccsize: Indicates the size of the cycle counter in bits.
* @ccitmin: minimum value that can be programmed in
* @s_ex_level: In secure state, indicates whether instruction tracing is
* supported for the corresponding Exception level.
* @ns_ex_level:In non-secure state, indicates whether instruction tracing is
* supported for the corresponding Exception level.
* @sticky_enable: true if ETM base configuration has been done.
* @boot_enable:True if we should start tracing at boot time.
* @os_unlock: True if access to management registers is allowed.
* @instrp0: Tracing of load and store instructions
* as P0 elements is supported.
* @trcbb: Indicates if the trace unit supports branch broadcast tracing.
* @trccond: If the trace unit supports conditional
* instruction tracing.
* @retstack: Indicates if the implementation supports a return stack.
* @trccci: Indicates if the trace unit supports cycle counting
* for instruction.
* @q_support: Q element support characteristics.
* @trc_error: Whether a trace unit can trace a system
* error exception.
* @syncpr: Indicates if an implementation has a fixed
* synchronization period.
* @stall_ctrl: Enables trace unit functionality that prevents trace
* unit buffer overflows.
* @sysstall: Does the system support stall control of the PE?
* @nooverflow: Indicate if overflow prevention is supported.
* @atbtrig: If the implementation can support ATB triggers
* @lpoverride: If the implementation can support low-power state over.
* @config: structure holding configuration parameters.
*/
struct etmv4_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
spinlock_t spinlock;
local_t mode;
int cpu;
u8 arch;
u8 nr_pe;
u8 nr_pe_cmp;
u8 nr_addr_cmp;
u8 nr_cntr;
u8 nr_ext_inp;
u8 numcidc;
u8 numvmidc;
u8 nrseqstate;
u8 nr_event;
u8 nr_resource;
u8 nr_ss_cmp;
u8 trcid;
u8 trcid_size;
u8 ts_size;
u8 ctxid_size;
u8 vmid_size;
u8 ccsize;
u8 ccitmin;
u8 s_ex_level;
u8 ns_ex_level;
u32 ext_inp;
u8 q_support;
bool sticky_enable;
bool boot_enable;
bool os_unlock;
bool instrp0;
bool trcbb;
bool trccond;
bool retstack;
bool trccci;
bool trc_error;
bool syncpr;
bool stallctl;
bool sysstall;
bool nooverflow;
bool atbtrig;
bool lpoverride;
struct etmv4_config config;
};
/* Address comparator access types */
@ -391,4 +414,7 @@ enum etm_addr_type {
ETM_ADDR_TYPE_START,
ETM_ADDR_TYPE_STOP,
};
extern const struct attribute_group *coresight_etmv4_groups[];
void etm4_config_trace_mode(struct etmv4_config *config);
#endif

View File

@ -221,7 +221,6 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "FUNNEL initialized\n");
return 0;
}

View File

@ -37,12 +37,42 @@
#define ETM_MODE_EXCL_KERN BIT(30)
#define ETM_MODE_EXCL_USER BIT(31)
#define coresight_simple_func(type, name, offset) \
static ssize_t name##_show(struct device *_dev, \
struct device_attribute *attr, char *buf) \
{ \
type *drvdata = dev_get_drvdata(_dev->parent); \
return scnprintf(buf, PAGE_SIZE, "0x%x\n", \
readl_relaxed(drvdata->base + offset)); \
} \
static DEVICE_ATTR_RO(name)
enum cs_mode {
CS_MODE_DISABLED,
CS_MODE_SYSFS,
CS_MODE_PERF,
};
/**
* struct cs_buffer - keep track of a recording session' specifics
* @cur: index of the current buffer
* @nr_pages: max number of pages granted to us
* @offset: offset within the current buffer
* @data_size: how much we collected in this run
* @lost: other than zero if we had a HW buffer wrap around
* @snapshot: is this run in snapshot mode
* @data_pages: a handle the ring buffer
*/
struct cs_buffers {
unsigned int cur;
unsigned int nr_pages;
unsigned long offset;
local_t data_size;
local_t lost;
bool snapshot;
void **data_pages;
};
static inline void CS_LOCK(void __iomem *addr)
{
do {

View File

@ -114,7 +114,6 @@ static int replicator_probe(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
dev_info(dev, "REPLICATOR initialized\n");
return 0;
out_disable_pm:

View File

@ -0,0 +1,920 @@
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* Description: CoreSight System Trace Macrocell driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Initial implementation by Pratik Patel
* (C) 2014-2015 Pratik Patel <pratikp@codeaurora.org>
*
* Serious refactoring, code cleanup and upgrading to the Coresight upstream
* framework by Mathieu Poirier
* (C) 2015-2016 Mathieu Poirier <mathieu.poirier@linaro.org>
*
* Guaranteed timing and support for various packet type coming from the
* generic STM API by Chunyan Zhang
* (C) 2015-2016 Chunyan Zhang <zhang.chunyan@linaro.org>
*/
#include <asm/local.h>
#include <linux/amba/bus.h>
#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include <linux/coresight-stm.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/of_address.h>
#include <linux/perf_event.h>
#include <linux/pm_runtime.h>
#include <linux/stm.h>
#include "coresight-priv.h"
#define STMDMASTARTR 0xc04
#define STMDMASTOPR 0xc08
#define STMDMASTATR 0xc0c
#define STMDMACTLR 0xc10
#define STMDMAIDR 0xcfc
#define STMHEER 0xd00
#define STMHETER 0xd20
#define STMHEBSR 0xd60
#define STMHEMCR 0xd64
#define STMHEMASTR 0xdf4
#define STMHEFEAT1R 0xdf8
#define STMHEIDR 0xdfc
#define STMSPER 0xe00
#define STMSPTER 0xe20
#define STMPRIVMASKR 0xe40
#define STMSPSCR 0xe60
#define STMSPMSCR 0xe64
#define STMSPOVERRIDER 0xe68
#define STMSPMOVERRIDER 0xe6c
#define STMSPTRIGCSR 0xe70
#define STMTCSR 0xe80
#define STMTSSTIMR 0xe84
#define STMTSFREQR 0xe8c
#define STMSYNCR 0xe90
#define STMAUXCR 0xe94
#define STMSPFEAT1R 0xea0
#define STMSPFEAT2R 0xea4
#define STMSPFEAT3R 0xea8
#define STMITTRIGGER 0xee8
#define STMITATBDATA0 0xeec
#define STMITATBCTR2 0xef0
#define STMITATBID 0xef4
#define STMITATBCTR0 0xef8
#define STM_32_CHANNEL 32
#define BYTES_PER_CHANNEL 256
#define STM_TRACE_BUF_SIZE 4096
#define STM_SW_MASTER_END 127
/* Register bit definition */
#define STMTCSR_BUSY_BIT 23
/* Reserve the first 10 channels for kernel usage */
#define STM_CHANNEL_OFFSET 0
enum stm_pkt_type {
STM_PKT_TYPE_DATA = 0x98,
STM_PKT_TYPE_FLAG = 0xE8,
STM_PKT_TYPE_TRIG = 0xF8,
};
#define stm_channel_addr(drvdata, ch) (drvdata->chs.base + \
(ch * BYTES_PER_CHANNEL))
#define stm_channel_off(type, opts) (type & ~opts)
static int boot_nr_channel;
/*
* Not really modular but using module_param is the easiest way to
* remain consistent with existing use cases for now.
*/
module_param_named(
boot_nr_channel, boot_nr_channel, int, S_IRUGO
);
/**
* struct channel_space - central management entity for extended ports
* @base: memory mapped base address where channels start.
* @guaraneed: is the channel delivery guaranteed.
*/
struct channel_space {
void __iomem *base;
unsigned long *guaranteed;
};
/**
* struct stm_drvdata - specifics associated to an STM component
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @atclk: optional clock for the core parts of the STM.
* @csdev: component vitals needed by the framework.
* @spinlock: only one at a time pls.
* @chs: the channels accociated to this STM.
* @stm: structure associated to the generic STM interface.
* @mode: this tracer's mode, i.e sysFS, or disabled.
* @traceid: value of the current ID for this component.
* @write_bytes: Maximus bytes this STM can write at a time.
* @stmsper: settings for register STMSPER.
* @stmspscr: settings for register STMSPSCR.
* @numsp: the total number of stimulus port support by this STM.
* @stmheer: settings for register STMHEER.
* @stmheter: settings for register STMHETER.
* @stmhebsr: settings for register STMHEBSR.
*/
struct stm_drvdata {
void __iomem *base;
struct device *dev;
struct clk *atclk;
struct coresight_device *csdev;
spinlock_t spinlock;
struct channel_space chs;
struct stm_data stm;
local_t mode;
u8 traceid;
u32 write_bytes;
u32 stmsper;
u32 stmspscr;
u32 numsp;
u32 stmheer;
u32 stmheter;
u32 stmhebsr;
};
static void stm_hwevent_enable_hw(struct stm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
writel_relaxed(drvdata->stmhebsr, drvdata->base + STMHEBSR);
writel_relaxed(drvdata->stmheter, drvdata->base + STMHETER);
writel_relaxed(drvdata->stmheer, drvdata->base + STMHEER);
writel_relaxed(0x01 | /* Enable HW event tracing */
0x04, /* Error detection on event tracing */
drvdata->base + STMHEMCR);
CS_LOCK(drvdata->base);
}
static void stm_port_enable_hw(struct stm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* ATB trigger enable on direct writes to TRIG locations */
writel_relaxed(0x10,
drvdata->base + STMSPTRIGCSR);
writel_relaxed(drvdata->stmspscr, drvdata->base + STMSPSCR);
writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER);
CS_LOCK(drvdata->base);
}
static void stm_enable_hw(struct stm_drvdata *drvdata)
{
if (drvdata->stmheer)
stm_hwevent_enable_hw(drvdata);
stm_port_enable_hw(drvdata);
CS_UNLOCK(drvdata->base);
/* 4096 byte between synchronisation packets */
writel_relaxed(0xFFF, drvdata->base + STMSYNCR);
writel_relaxed((drvdata->traceid << 16 | /* trace id */
0x02 | /* timestamp enable */
0x01), /* global STM enable */
drvdata->base + STMTCSR);
CS_LOCK(drvdata->base);
}
static int stm_enable(struct coresight_device *csdev,
struct perf_event_attr *attr, u32 mode)
{
u32 val;
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (mode != CS_MODE_SYSFS)
return -EINVAL;
val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
/* Someone is already using the tracer */
if (val)
return -EBUSY;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
stm_enable_hw(drvdata);
spin_unlock(&drvdata->spinlock);
dev_info(drvdata->dev, "STM tracing enabled\n");
return 0;
}
static void stm_hwevent_disable_hw(struct stm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
writel_relaxed(0x0, drvdata->base + STMHEMCR);
writel_relaxed(0x0, drvdata->base + STMHEER);
writel_relaxed(0x0, drvdata->base + STMHETER);
CS_LOCK(drvdata->base);
}
static void stm_port_disable_hw(struct stm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
writel_relaxed(0x0, drvdata->base + STMSPER);
writel_relaxed(0x0, drvdata->base + STMSPTRIGCSR);
CS_LOCK(drvdata->base);
}
static void stm_disable_hw(struct stm_drvdata *drvdata)
{
u32 val;
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + STMTCSR);
val &= ~0x1; /* clear global STM enable [0] */
writel_relaxed(val, drvdata->base + STMTCSR);
CS_LOCK(drvdata->base);
stm_port_disable_hw(drvdata);
if (drvdata->stmheer)
stm_hwevent_disable_hw(drvdata);
}
static void stm_disable(struct coresight_device *csdev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/*
* For as long as the tracer isn't disabled another entity can't
* change its status. As such we can read the status here without
* fearing it will change under us.
*/
if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
spin_lock(&drvdata->spinlock);
stm_disable_hw(drvdata);
spin_unlock(&drvdata->spinlock);
/* Wait until the engine has completely stopped */
coresight_timeout(drvdata, STMTCSR, STMTCSR_BUSY_BIT, 0);
pm_runtime_put(drvdata->dev);
local_set(&drvdata->mode, CS_MODE_DISABLED);
dev_info(drvdata->dev, "STM tracing disabled\n");
}
}
static int stm_trace_id(struct coresight_device *csdev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return drvdata->traceid;
}
static const struct coresight_ops_source stm_source_ops = {
.trace_id = stm_trace_id,
.enable = stm_enable,
.disable = stm_disable,
};
static const struct coresight_ops stm_cs_ops = {
.source_ops = &stm_source_ops,
};
static inline bool stm_addr_unaligned(const void *addr, u8 write_bytes)
{
return ((unsigned long)addr & (write_bytes - 1));
}
static void stm_send(void *addr, const void *data, u32 size, u8 write_bytes)
{
u8 paload[8];
if (stm_addr_unaligned(data, write_bytes)) {
memcpy(paload, data, size);
data = paload;
}
/* now we are 64bit/32bit aligned */
switch (size) {
#ifdef CONFIG_64BIT
case 8:
writeq_relaxed(*(u64 *)data, addr);
break;
#endif
case 4:
writel_relaxed(*(u32 *)data, addr);
break;
case 2:
writew_relaxed(*(u16 *)data, addr);
break;
case 1:
writeb_relaxed(*(u8 *)data, addr);
break;
default:
break;
}
}
static int stm_generic_link(struct stm_data *stm_data,
unsigned int master, unsigned int channel)
{
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
if (!drvdata || !drvdata->csdev)
return -EINVAL;
return coresight_enable(drvdata->csdev);
}
static void stm_generic_unlink(struct stm_data *stm_data,
unsigned int master, unsigned int channel)
{
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
if (!drvdata || !drvdata->csdev)
return;
stm_disable(drvdata->csdev);
}
static long stm_generic_set_options(struct stm_data *stm_data,
unsigned int master,
unsigned int channel,
unsigned int nr_chans,
unsigned long options)
{
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
if (!(drvdata && local_read(&drvdata->mode)))
return -EINVAL;
if (channel >= drvdata->numsp)
return -EINVAL;
switch (options) {
case STM_OPTION_GUARANTEED:
set_bit(channel, drvdata->chs.guaranteed);
break;
case STM_OPTION_INVARIANT:
clear_bit(channel, drvdata->chs.guaranteed);
break;
default:
return -EINVAL;
}
return 0;
}
static ssize_t stm_generic_packet(struct stm_data *stm_data,
unsigned int master,
unsigned int channel,
unsigned int packet,
unsigned int flags,
unsigned int size,
const unsigned char *payload)
{
unsigned long ch_addr;
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
if (!(drvdata && local_read(&drvdata->mode)))
return 0;
if (channel >= drvdata->numsp)
return 0;
ch_addr = (unsigned long)stm_channel_addr(drvdata, channel);
flags = (flags == STP_PACKET_TIMESTAMPED) ? STM_FLAG_TIMESTAMPED : 0;
flags |= test_bit(channel, drvdata->chs.guaranteed) ?
STM_FLAG_GUARANTEED : 0;
if (size > drvdata->write_bytes)
size = drvdata->write_bytes;
else
size = rounddown_pow_of_two(size);
switch (packet) {
case STP_PACKET_FLAG:
ch_addr |= stm_channel_off(STM_PKT_TYPE_FLAG, flags);
/*
* The generic STM core sets a size of '0' on flag packets.
* As such send a flag packet of size '1' and tell the
* core we did so.
*/
stm_send((void *)ch_addr, payload, 1, drvdata->write_bytes);
size = 1;
break;
case STP_PACKET_DATA:
ch_addr |= stm_channel_off(STM_PKT_TYPE_DATA, flags);
stm_send((void *)ch_addr, payload, size,
drvdata->write_bytes);
break;
default:
return -ENOTSUPP;
}
return size;
}
static ssize_t hwevent_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->stmheer;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t hwevent_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
int ret = 0;
ret = kstrtoul(buf, 16, &val);
if (ret)
return -EINVAL;
drvdata->stmheer = val;
/* HW event enable and trigger go hand in hand */
drvdata->stmheter = val;
return size;
}
static DEVICE_ATTR_RW(hwevent_enable);
static ssize_t hwevent_select_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->stmhebsr;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t hwevent_select_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
int ret = 0;
ret = kstrtoul(buf, 16, &val);
if (ret)
return -EINVAL;
drvdata->stmhebsr = val;
return size;
}
static DEVICE_ATTR_RW(hwevent_select);
static ssize_t port_select_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (!local_read(&drvdata->mode)) {
val = drvdata->stmspscr;
} else {
spin_lock(&drvdata->spinlock);
val = readl_relaxed(drvdata->base + STMSPSCR);
spin_unlock(&drvdata->spinlock);
}
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t port_select_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val, stmsper;
int ret = 0;
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
spin_lock(&drvdata->spinlock);
drvdata->stmspscr = val;
if (local_read(&drvdata->mode)) {
CS_UNLOCK(drvdata->base);
/* Process as per ARM's TRM recommendation */
stmsper = readl_relaxed(drvdata->base + STMSPER);
writel_relaxed(0x0, drvdata->base + STMSPER);
writel_relaxed(drvdata->stmspscr, drvdata->base + STMSPSCR);
writel_relaxed(stmsper, drvdata->base + STMSPER);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(port_select);
static ssize_t port_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (!local_read(&drvdata->mode)) {
val = drvdata->stmsper;
} else {
spin_lock(&drvdata->spinlock);
val = readl_relaxed(drvdata->base + STMSPER);
spin_unlock(&drvdata->spinlock);
}
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t port_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
int ret = 0;
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
spin_lock(&drvdata->spinlock);
drvdata->stmsper = val;
if (local_read(&drvdata->mode)) {
CS_UNLOCK(drvdata->base);
writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(port_enable);
static ssize_t traceid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long val;
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = drvdata->traceid;
return sprintf(buf, "%#lx\n", val);
}
static ssize_t traceid_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
/* traceid field is 7bit wide on STM32 */
drvdata->traceid = val & 0x7f;
return size;
}
static DEVICE_ATTR_RW(traceid);
#define coresight_stm_simple_func(name, offset) \
coresight_simple_func(struct stm_drvdata, name, offset)
coresight_stm_simple_func(tcsr, STMTCSR);
coresight_stm_simple_func(tsfreqr, STMTSFREQR);
coresight_stm_simple_func(syncr, STMSYNCR);
coresight_stm_simple_func(sper, STMSPER);
coresight_stm_simple_func(spter, STMSPTER);
coresight_stm_simple_func(privmaskr, STMPRIVMASKR);
coresight_stm_simple_func(spscr, STMSPSCR);
coresight_stm_simple_func(spmscr, STMSPMSCR);
coresight_stm_simple_func(spfeat1r, STMSPFEAT1R);
coresight_stm_simple_func(spfeat2r, STMSPFEAT2R);
coresight_stm_simple_func(spfeat3r, STMSPFEAT3R);
coresight_stm_simple_func(devid, CORESIGHT_DEVID);
static struct attribute *coresight_stm_attrs[] = {
&dev_attr_hwevent_enable.attr,
&dev_attr_hwevent_select.attr,
&dev_attr_port_enable.attr,
&dev_attr_port_select.attr,
&dev_attr_traceid.attr,
NULL,
};
static struct attribute *coresight_stm_mgmt_attrs[] = {
&dev_attr_tcsr.attr,
&dev_attr_tsfreqr.attr,
&dev_attr_syncr.attr,
&dev_attr_sper.attr,
&dev_attr_spter.attr,
&dev_attr_privmaskr.attr,
&dev_attr_spscr.attr,
&dev_attr_spmscr.attr,
&dev_attr_spfeat1r.attr,
&dev_attr_spfeat2r.attr,
&dev_attr_spfeat3r.attr,
&dev_attr_devid.attr,
NULL,
};
static const struct attribute_group coresight_stm_group = {
.attrs = coresight_stm_attrs,
};
static const struct attribute_group coresight_stm_mgmt_group = {
.attrs = coresight_stm_mgmt_attrs,
.name = "mgmt",
};
static const struct attribute_group *coresight_stm_groups[] = {
&coresight_stm_group,
&coresight_stm_mgmt_group,
NULL,
};
static int stm_get_resource_byname(struct device_node *np,
char *ch_base, struct resource *res)
{
const char *name = NULL;
int index = 0, found = 0;
while (!of_property_read_string_index(np, "reg-names", index, &name)) {
if (strcmp(ch_base, name)) {
index++;
continue;
}
/* We have a match and @index is where it's at */
found = 1;
break;
}
if (!found)
return -EINVAL;
return of_address_to_resource(np, index, res);
}
static u32 stm_fundamental_data_size(struct stm_drvdata *drvdata)
{
u32 stmspfeat2r;
if (!IS_ENABLED(CONFIG_64BIT))
return 4;
stmspfeat2r = readl_relaxed(drvdata->base + STMSPFEAT2R);
/*
* bit[15:12] represents the fundamental data size
* 0 - 32-bit data
* 1 - 64-bit data
*/
return BMVAL(stmspfeat2r, 12, 15) ? 8 : 4;
}
static u32 stm_num_stimulus_port(struct stm_drvdata *drvdata)
{
u32 numsp;
numsp = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
/*
* NUMPS in STMDEVID is 17 bit long and if equal to 0x0,
* 32 stimulus ports are supported.
*/
numsp &= 0x1ffff;
if (!numsp)
numsp = STM_32_CHANNEL;
return numsp;
}
static void stm_init_default_data(struct stm_drvdata *drvdata)
{
/* Don't use port selection */
drvdata->stmspscr = 0x0;
/*
* Enable all channel regardless of their number. When port
* selection isn't used (see above) STMSPER applies to all
* 32 channel group available, hence setting all 32 bits to 1
*/
drvdata->stmsper = ~0x0;
/*
* The trace ID value for *ETM* tracers start at CPU_ID * 2 + 0x10 and
* anything equal to or higher than 0x70 is reserved. Since 0x00 is
* also reserved the STM trace ID needs to be higher than 0x00 and
* lowner than 0x10.
*/
drvdata->traceid = 0x1;
/* Set invariant transaction timing on all channels */
bitmap_clear(drvdata->chs.guaranteed, 0, drvdata->numsp);
}
static void stm_init_generic_data(struct stm_drvdata *drvdata)
{
drvdata->stm.name = dev_name(drvdata->dev);
/*
* MasterIDs are assigned at HW design phase. As such the core is
* using a single master for interaction with this device.
*/
drvdata->stm.sw_start = 1;
drvdata->stm.sw_end = 1;
drvdata->stm.hw_override = true;
drvdata->stm.sw_nchannels = drvdata->numsp;
drvdata->stm.packet = stm_generic_packet;
drvdata->stm.link = stm_generic_link;
drvdata->stm.unlink = stm_generic_unlink;
drvdata->stm.set_options = stm_generic_set_options;
}
static int stm_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
void __iomem *base;
unsigned long *guaranteed;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct stm_drvdata *drvdata;
struct resource *res = &adev->res;
struct resource ch_res;
size_t res_size, bitmap_size;
struct coresight_desc *desc;
struct device_node *np = adev->dev.of_node;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
if (!IS_ERR(drvdata->atclk)) {
ret = clk_prepare_enable(drvdata->atclk);
if (ret)
return ret;
}
dev_set_drvdata(dev, drvdata);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res);
if (ret)
return ret;
base = devm_ioremap_resource(dev, &ch_res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->chs.base = base;
drvdata->write_bytes = stm_fundamental_data_size(drvdata);
if (boot_nr_channel) {
drvdata->numsp = boot_nr_channel;
res_size = min((resource_size_t)(boot_nr_channel *
BYTES_PER_CHANNEL), resource_size(res));
} else {
drvdata->numsp = stm_num_stimulus_port(drvdata);
res_size = min((resource_size_t)(drvdata->numsp *
BYTES_PER_CHANNEL), resource_size(res));
}
bitmap_size = BITS_TO_LONGS(drvdata->numsp) * sizeof(long);
guaranteed = devm_kzalloc(dev, bitmap_size, GFP_KERNEL);
if (!guaranteed)
return -ENOMEM;
drvdata->chs.guaranteed = guaranteed;
spin_lock_init(&drvdata->spinlock);
stm_init_default_data(drvdata);
stm_init_generic_data(drvdata);
if (stm_register_device(dev, &drvdata->stm, THIS_MODULE)) {
dev_info(dev,
"stm_register_device failed, probing deffered\n");
return -EPROBE_DEFER;
}
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc) {
ret = -ENOMEM;
goto stm_unregister;
}
desc->type = CORESIGHT_DEV_TYPE_SOURCE;
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
desc->ops = &stm_cs_ops;
desc->pdata = pdata;
desc->dev = dev;
desc->groups = coresight_stm_groups;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto stm_unregister;
}
pm_runtime_put(&adev->dev);
dev_info(dev, "%s initialized\n", (char *)id->data);
return 0;
stm_unregister:
stm_unregister_device(&drvdata->stm);
return ret;
}
#ifdef CONFIG_PM
static int stm_runtime_suspend(struct device *dev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev);
if (drvdata && !IS_ERR(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
return 0;
}
static int stm_runtime_resume(struct device *dev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev);
if (drvdata && !IS_ERR(drvdata->atclk))
clk_prepare_enable(drvdata->atclk);
return 0;
}
#endif
static const struct dev_pm_ops stm_dev_pm_ops = {
SET_RUNTIME_PM_OPS(stm_runtime_suspend, stm_runtime_resume, NULL)
};
static struct amba_id stm_ids[] = {
{
.id = 0x0003b962,
.mask = 0x0003ffff,
.data = "STM32",
},
{ 0, 0},
};
static struct amba_driver stm_driver = {
.drv = {
.name = "coresight-stm",
.owner = THIS_MODULE,
.pm = &stm_dev_pm_ops,
.suppress_bind_attrs = true,
},
.probe = stm_probe,
.id_table = stm_ids,
};
builtin_amba_driver(stm_driver);

View File

@ -0,0 +1,604 @@
/*
* Copyright(C) 2016 Linaro Limited. All rights reserved.
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/circ_buf.h>
#include <linux/coresight.h>
#include <linux/perf_event.h>
#include <linux/slab.h>
#include "coresight-priv.h"
#include "coresight-tmc.h"
void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* Wait for TMCSReady bit to be set */
tmc_wait_for_tmcready(drvdata);
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
TMC_FFCR_TRIGON_TRIGIN,
drvdata->base + TMC_FFCR);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
{
char *bufp;
u32 read_data;
int i;
bufp = drvdata->buf;
while (1) {
for (i = 0; i < drvdata->memwidth; i++) {
read_data = readl_relaxed(drvdata->base + TMC_RRD);
if (read_data == 0xFFFFFFFF)
return;
memcpy(bufp, &read_data, 4);
bufp += 4;
}
}
}
static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
/*
* When operating in sysFS mode the content of the buffer needs to be
* read before the TMC is disabled.
*/
if (local_read(&drvdata->mode) == CS_MODE_SYSFS)
tmc_etb_dump_hw(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* Wait for TMCSReady bit to be set */
tmc_wait_for_tmcready(drvdata);
writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
drvdata->base + TMC_FFCR);
writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
{
int ret = 0;
bool used = false;
char *buf = NULL;
long val;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/* This shouldn't be happening */
if (WARN_ON(mode != CS_MODE_SYSFS))
return -EINVAL;
/*
* If we don't have a buffer release the lock and allocate memory.
* Otherwise keep the lock and move along.
*/
spin_lock_irqsave(&drvdata->spinlock, flags);
if (!drvdata->buf) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Allocating the memory here while outside of the spinlock */
buf = kzalloc(drvdata->size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Let's try again */
spin_lock_irqsave(&drvdata->spinlock, flags);
}
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
val = local_xchg(&drvdata->mode, mode);
/*
* In sysFS mode we can have multiple writers per sink. Since this
* sink is already enabled no memory is needed and the HW need not be
* touched.
*/
if (val == CS_MODE_SYSFS)
goto out;
/*
* If drvdata::buf isn't NULL, memory was allocated for a previous
* trace run but wasn't read. If so simply zero-out the memory.
* Otherwise use the memory allocated above.
*
* The memory is freed when users read the buffer using the
* /dev/xyz.{etf|etb} interface. See tmc_read_unprepare_etf() for
* details.
*/
if (drvdata->buf) {
memset(drvdata->buf, 0, drvdata->size);
} else {
used = true;
drvdata->buf = buf;
}
tmc_etb_enable_hw(drvdata);
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free memory outside the spinlock if need be */
if (!used && buf)
kfree(buf);
if (!ret)
dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
return ret;
}
static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode)
{
int ret = 0;
long val;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/* This shouldn't be happening */
if (WARN_ON(mode != CS_MODE_PERF))
return -EINVAL;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EINVAL;
goto out;
}
val = local_xchg(&drvdata->mode, mode);
/*
* In Perf mode there can be only one writer per sink. There
* is also no need to continue if the ETB/ETR is already operated
* from sysFS.
*/
if (val != CS_MODE_DISABLED) {
ret = -EINVAL;
goto out;
}
tmc_etb_enable_hw(drvdata);
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
{
switch (mode) {
case CS_MODE_SYSFS:
return tmc_enable_etf_sink_sysfs(csdev, mode);
case CS_MODE_PERF:
return tmc_enable_etf_sink_perf(csdev, mode);
}
/* We shouldn't be here */
return -EINVAL;
}
static void tmc_disable_etf_sink(struct coresight_device *csdev)
{
long val;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return;
}
val = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
/* Disable the TMC only if it needs to */
if (val != CS_MODE_DISABLED)
tmc_etb_disable_hw(drvdata);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n");
}
static int tmc_enable_etf_link(struct coresight_device *csdev,
int inport, int outport)
{
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
tmc_etf_enable_hw(drvdata);
local_set(&drvdata->mode, CS_MODE_SYSFS);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC-ETF enabled\n");
return 0;
}
static void tmc_disable_etf_link(struct coresight_device *csdev,
int inport, int outport)
{
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return;
}
tmc_etf_disable_hw(drvdata);
local_set(&drvdata->mode, CS_MODE_DISABLED);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC disabled\n");
}
static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu,
void **pages, int nr_pages, bool overwrite)
{
int node;
struct cs_buffers *buf;
if (cpu == -1)
cpu = smp_processor_id();
node = cpu_to_node(cpu);
/* Allocate memory structure for interaction with Perf */
buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node);
if (!buf)
return NULL;
buf->snapshot = overwrite;
buf->nr_pages = nr_pages;
buf->data_pages = pages;
return buf;
}
static void tmc_free_etf_buffer(void *config)
{
struct cs_buffers *buf = config;
kfree(buf);
}
static int tmc_set_etf_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle,
void *sink_config)
{
int ret = 0;
unsigned long head;
struct cs_buffers *buf = sink_config;
/* wrap head around to the amount of space we have */
head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
/* find the page to write to */
buf->cur = head / PAGE_SIZE;
/* and offset within that page */
buf->offset = head % PAGE_SIZE;
local_set(&buf->data_size, 0);
return ret;
}
static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle,
void *sink_config, bool *lost)
{
long size = 0;
struct cs_buffers *buf = sink_config;
if (buf) {
/*
* In snapshot mode ->data_size holds the new address of the
* ring buffer's head. The size itself is the whole address
* range since we want the latest information.
*/
if (buf->snapshot)
handle->head = local_xchg(&buf->data_size,
buf->nr_pages << PAGE_SHIFT);
/*
* Tell the tracer PMU how much we got in this run and if
* something went wrong along the way. Nobody else can use
* this cs_buffers instance until we are done. As such
* resetting parameters here and squaring off with the ring
* buffer API in the tracer PMU is fine.
*/
*lost = !!local_xchg(&buf->lost, 0);
size = local_xchg(&buf->data_size, 0);
}
return size;
}
static void tmc_update_etf_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle,
void *sink_config)
{
int i, cur;
u32 *buf_ptr;
u32 read_ptr, write_ptr;
u32 status, to_read;
unsigned long offset;
struct cs_buffers *buf = sink_config;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (!buf)
return;
/* This shouldn't happen */
if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF))
return;
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
read_ptr = readl_relaxed(drvdata->base + TMC_RRP);
write_ptr = readl_relaxed(drvdata->base + TMC_RWP);
/*
* Get a hold of the status register and see if a wrap around
* has occurred. If so adjust things accordingly.
*/
status = readl_relaxed(drvdata->base + TMC_STS);
if (status & TMC_STS_FULL) {
local_inc(&buf->lost);
to_read = drvdata->size;
} else {
to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size);
}
/*
* The TMC RAM buffer may be bigger than the space available in the
* perf ring buffer (handle->size). If so advance the RRP so that we
* get the latest trace data.
*/
if (to_read > handle->size) {
u32 mask = 0;
/*
* The value written to RRP must be byte-address aligned to
* the width of the trace memory databus _and_ to a frame
* boundary (16 byte), whichever is the biggest. For example,
* for 32-bit, 64-bit and 128-bit wide trace memory, the four
* LSBs must be 0s. For 256-bit wide trace memory, the five
* LSBs must be 0s.
*/
switch (drvdata->memwidth) {
case TMC_MEM_INTF_WIDTH_32BITS:
case TMC_MEM_INTF_WIDTH_64BITS:
case TMC_MEM_INTF_WIDTH_128BITS:
mask = GENMASK(31, 5);
break;
case TMC_MEM_INTF_WIDTH_256BITS:
mask = GENMASK(31, 6);
break;
}
/*
* Make sure the new size is aligned in accordance with the
* requirement explained above.
*/
to_read = handle->size & mask;
/* Move the RAM read pointer up */
read_ptr = (write_ptr + drvdata->size) - to_read;
/* Make sure we are still within our limits */
if (read_ptr > (drvdata->size - 1))
read_ptr -= drvdata->size;
/* Tell the HW */
writel_relaxed(read_ptr, drvdata->base + TMC_RRP);
local_inc(&buf->lost);
}
cur = buf->cur;
offset = buf->offset;
/* for every byte to read */
for (i = 0; i < to_read; i += 4) {
buf_ptr = buf->data_pages[cur] + offset;
*buf_ptr = readl_relaxed(drvdata->base + TMC_RRD);
offset += 4;
if (offset >= PAGE_SIZE) {
offset = 0;
cur++;
/* wrap around at the end of the buffer */
cur &= buf->nr_pages - 1;
}
}
/*
* In snapshot mode all we have to do is communicate to
* perf_aux_output_end() the address of the current head. In full
* trace mode the same function expects a size to move rb->aux_head
* forward.
*/
if (buf->snapshot)
local_set(&buf->data_size, (cur * PAGE_SIZE) + offset);
else
local_add(to_read, &buf->data_size);
CS_LOCK(drvdata->base);
}
static const struct coresight_ops_sink tmc_etf_sink_ops = {
.enable = tmc_enable_etf_sink,
.disable = tmc_disable_etf_sink,
.alloc_buffer = tmc_alloc_etf_buffer,
.free_buffer = tmc_free_etf_buffer,
.set_buffer = tmc_set_etf_buffer,
.reset_buffer = tmc_reset_etf_buffer,
.update_buffer = tmc_update_etf_buffer,
};
static const struct coresight_ops_link tmc_etf_link_ops = {
.enable = tmc_enable_etf_link,
.disable = tmc_disable_etf_link,
};
const struct coresight_ops tmc_etb_cs_ops = {
.sink_ops = &tmc_etf_sink_ops,
};
const struct coresight_ops tmc_etf_cs_ops = {
.sink_ops = &tmc_etf_sink_ops,
.link_ops = &tmc_etf_link_ops,
};
int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
{
long val;
enum tmc_mode mode;
int ret = 0;
unsigned long flags;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
drvdata->config_type != TMC_CONFIG_TYPE_ETF))
return -EINVAL;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
/* There is no point in reading a TMC in HW FIFO mode */
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode != TMC_MODE_CIRCULAR_BUFFER) {
ret = -EINVAL;
goto out;
}
val = local_read(&drvdata->mode);
/* Don't interfere if operated from Perf */
if (val == CS_MODE_PERF) {
ret = -EINVAL;
goto out;
}
/* If drvdata::buf is NULL the trace data has been read already */
if (drvdata->buf == NULL) {
ret = -EINVAL;
goto out;
}
/* Disable the TMC if need be */
if (val == CS_MODE_SYSFS)
tmc_etb_disable_hw(drvdata);
drvdata->reading = true;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
{
char *buf = NULL;
enum tmc_mode mode;
unsigned long flags;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
drvdata->config_type != TMC_CONFIG_TYPE_ETF))
return -EINVAL;
spin_lock_irqsave(&drvdata->spinlock, flags);
/* There is no point in reading a TMC in HW FIFO mode */
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode != TMC_MODE_CIRCULAR_BUFFER) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EINVAL;
}
/* Re-enable the TMC if need be */
if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
/*
* The trace run will continue with the same allocated trace
* buffer. As such zero-out the buffer so that we don't end
* up with stale data.
*
* Since the tracer is still enabled drvdata::buf
* can't be NULL.
*/
memset(drvdata->buf, 0, drvdata->size);
tmc_etb_enable_hw(drvdata);
} else {
/*
* The ETB/ETF is not tracing and the buffer was just read.
* As such prepare to free the trace buffer.
*/
buf = drvdata->buf;
drvdata->buf = NULL;
}
drvdata->reading = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/*
* Free allocated memory outside of the spinlock. There is no need
* to assert the validity of 'buf' since calling kfree(NULL) is safe.
*/
kfree(buf);
return 0;
}

View File

@ -0,0 +1,329 @@
/*
* Copyright(C) 2016 Linaro Limited. All rights reserved.
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/coresight.h>
#include <linux/dma-mapping.h>
#include "coresight-priv.h"
#include "coresight-tmc.h"
void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{
u32 axictl;
/* Zero out the memory to help with debug */
memset(drvdata->vaddr, 0, drvdata->size);
CS_UNLOCK(drvdata->base);
/* Wait for TMCSReady bit to be set */
tmc_wait_for_tmcready(drvdata);
writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
axictl |= TMC_AXICTL_WR_BURST_16;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
axictl = (axictl &
~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
TMC_AXICTL_PROT_CTL_B1;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
TMC_FFCR_TRIGON_TRIGIN,
drvdata->base + TMC_FFCR);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
{
u32 rwp, val;
rwp = readl_relaxed(drvdata->base + TMC_RWP);
val = readl_relaxed(drvdata->base + TMC_STS);
/* How much memory do we still have */
if (val & BIT(0))
drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
else
drvdata->buf = drvdata->vaddr;
}
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
/*
* When operating in sysFS mode the content of the buffer needs to be
* read before the TMC is disabled.
*/
if (local_read(&drvdata->mode) == CS_MODE_SYSFS)
tmc_etr_dump_hw(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
{
int ret = 0;
bool used = false;
long val;
unsigned long flags;
void __iomem *vaddr = NULL;
dma_addr_t paddr;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/* This shouldn't be happening */
if (WARN_ON(mode != CS_MODE_SYSFS))
return -EINVAL;
/*
* If we don't have a buffer release the lock and allocate memory.
* Otherwise keep the lock and move along.
*/
spin_lock_irqsave(&drvdata->spinlock, flags);
if (!drvdata->vaddr) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/*
* Contiguous memory can't be allocated while a spinlock is
* held. As such allocate memory here and free it if a buffer
* has already been allocated (from a previous session).
*/
vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
&paddr, GFP_KERNEL);
if (!vaddr)
return -ENOMEM;
/* Let's try again */
spin_lock_irqsave(&drvdata->spinlock, flags);
}
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
val = local_xchg(&drvdata->mode, mode);
/*
* In sysFS mode we can have multiple writers per sink. Since this
* sink is already enabled no memory is needed and the HW need not be
* touched.
*/
if (val == CS_MODE_SYSFS)
goto out;
/*
* If drvdata::buf == NULL, use the memory allocated above.
* Otherwise a buffer still exists from a previous session, so
* simply use that.
*/
if (drvdata->buf == NULL) {
used = true;
drvdata->vaddr = vaddr;
drvdata->paddr = paddr;
drvdata->buf = drvdata->vaddr;
}
memset(drvdata->vaddr, 0, drvdata->size);
tmc_etr_enable_hw(drvdata);
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free memory outside the spinlock if need be */
if (!used && vaddr)
dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
if (!ret)
dev_info(drvdata->dev, "TMC-ETR enabled\n");
return ret;
}
static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode)
{
int ret = 0;
long val;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/* This shouldn't be happening */
if (WARN_ON(mode != CS_MODE_PERF))
return -EINVAL;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EINVAL;
goto out;
}
val = local_xchg(&drvdata->mode, mode);
/*
* In Perf mode there can be only one writer per sink. There
* is also no need to continue if the ETR is already operated
* from sysFS.
*/
if (val != CS_MODE_DISABLED) {
ret = -EINVAL;
goto out;
}
tmc_etr_enable_hw(drvdata);
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
{
switch (mode) {
case CS_MODE_SYSFS:
return tmc_enable_etr_sink_sysfs(csdev, mode);
case CS_MODE_PERF:
return tmc_enable_etr_sink_perf(csdev, mode);
}
/* We shouldn't be here */
return -EINVAL;
}
static void tmc_disable_etr_sink(struct coresight_device *csdev)
{
long val;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return;
}
val = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
/* Disable the TMC only if it needs to */
if (val != CS_MODE_DISABLED)
tmc_etr_disable_hw(drvdata);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC-ETR disabled\n");
}
static const struct coresight_ops_sink tmc_etr_sink_ops = {
.enable = tmc_enable_etr_sink,
.disable = tmc_disable_etr_sink,
};
const struct coresight_ops tmc_etr_cs_ops = {
.sink_ops = &tmc_etr_sink_ops,
};
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
int ret = 0;
long val;
unsigned long flags;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
val = local_read(&drvdata->mode);
/* Don't interfere if operated from Perf */
if (val == CS_MODE_PERF) {
ret = -EINVAL;
goto out;
}
/* If drvdata::buf is NULL the trace data has been read already */
if (drvdata->buf == NULL) {
ret = -EINVAL;
goto out;
}
/* Disable the TMC if need be */
if (val == CS_MODE_SYSFS)
tmc_etr_disable_hw(drvdata);
drvdata->reading = true;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{
unsigned long flags;
dma_addr_t paddr;
void __iomem *vaddr = NULL;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
spin_lock_irqsave(&drvdata->spinlock, flags);
/* RE-enable the TMC if need be */
if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
/*
* The trace run will continue with the same allocated trace
* buffer. As such zero-out the buffer so that we don't end
* up with stale data.
*
* Since the tracer is still enabled drvdata::buf
* can't be NULL.
*/
memset(drvdata->buf, 0, drvdata->size);
tmc_etr_enable_hw(drvdata);
} else {
/*
* The ETR is not tracing and the buffer was just read.
* As such prepare to free the trace buffer.
*/
vaddr = drvdata->vaddr;
paddr = drvdata->paddr;
drvdata->buf = NULL;
}
drvdata->reading = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free allocated memory out side of the spinlock */
if (vaddr)
dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
return 0;
}

View File

@ -30,127 +30,27 @@
#include <linux/amba/bus.h>
#include "coresight-priv.h"
#include "coresight-tmc.h"
#define TMC_RSZ 0x004
#define TMC_STS 0x00c
#define TMC_RRD 0x010
#define TMC_RRP 0x014
#define TMC_RWP 0x018
#define TMC_TRG 0x01c
#define TMC_CTL 0x020
#define TMC_RWD 0x024
#define TMC_MODE 0x028
#define TMC_LBUFLEVEL 0x02c
#define TMC_CBUFLEVEL 0x030
#define TMC_BUFWM 0x034
#define TMC_RRPHI 0x038
#define TMC_RWPHI 0x03c
#define TMC_AXICTL 0x110
#define TMC_DBALO 0x118
#define TMC_DBAHI 0x11c
#define TMC_FFSR 0x300
#define TMC_FFCR 0x304
#define TMC_PSCR 0x308
#define TMC_ITMISCOP0 0xee0
#define TMC_ITTRFLIN 0xee8
#define TMC_ITATBDATA0 0xeec
#define TMC_ITATBCTR2 0xef0
#define TMC_ITATBCTR1 0xef4
#define TMC_ITATBCTR0 0xef8
/* register description */
/* TMC_CTL - 0x020 */
#define TMC_CTL_CAPT_EN BIT(0)
/* TMC_STS - 0x00C */
#define TMC_STS_TRIGGERED BIT(1)
/* TMC_AXICTL - 0x110 */
#define TMC_AXICTL_PROT_CTL_B0 BIT(0)
#define TMC_AXICTL_PROT_CTL_B1 BIT(1)
#define TMC_AXICTL_SCT_GAT_MODE BIT(7)
#define TMC_AXICTL_WR_BURST_LEN 0xF00
/* TMC_FFCR - 0x304 */
#define TMC_FFCR_EN_FMT BIT(0)
#define TMC_FFCR_EN_TI BIT(1)
#define TMC_FFCR_FON_FLIN BIT(4)
#define TMC_FFCR_FON_TRIG_EVT BIT(5)
#define TMC_FFCR_FLUSHMAN BIT(6)
#define TMC_FFCR_TRIGON_TRIGIN BIT(8)
#define TMC_FFCR_STOP_ON_FLUSH BIT(12)
#define TMC_STS_TRIGGERED_BIT 2
#define TMC_FFCR_FLUSHMAN_BIT 6
enum tmc_config_type {
TMC_CONFIG_TYPE_ETB,
TMC_CONFIG_TYPE_ETR,
TMC_CONFIG_TYPE_ETF,
};
enum tmc_mode {
TMC_MODE_CIRCULAR_BUFFER,
TMC_MODE_SOFTWARE_FIFO,
TMC_MODE_HARDWARE_FIFO,
};
enum tmc_mem_intf_width {
TMC_MEM_INTF_WIDTH_32BITS = 0x2,
TMC_MEM_INTF_WIDTH_64BITS = 0x3,
TMC_MEM_INTF_WIDTH_128BITS = 0x4,
TMC_MEM_INTF_WIDTH_256BITS = 0x5,
};
/**
* struct tmc_drvdata - specifics associated to an TMC component
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
* @spinlock: only one at a time pls.
* @read_count: manages preparation of buffer for reading.
* @buf: area of memory where trace data get sent.
* @paddr: DMA start location in RAM.
* @vaddr: virtual representation of @paddr.
* @size: @buf size.
* @enable: this TMC is being used.
* @config_type: TMC variant, must be of type @tmc_config_type.
* @trigger_cntr: amount of words to store after a trigger.
*/
struct tmc_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct miscdevice miscdev;
spinlock_t spinlock;
int read_count;
bool reading;
char *buf;
dma_addr_t paddr;
void *vaddr;
u32 size;
bool enable;
enum tmc_config_type config_type;
u32 trigger_cntr;
};
static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
{
/* Ensure formatter, unformatter and hardware fifo are empty */
if (coresight_timeout(drvdata->base,
TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) {
TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n",
TMC_STS);
}
}
static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
{
u32 ffcr;
ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
ffcr |= TMC_FFCR_STOP_ON_FLUSH;
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
ffcr |= TMC_FFCR_FLUSHMAN;
ffcr |= BIT(TMC_FFCR_FLUSHMAN_BIT);
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
/* Ensure flush completes */
if (coresight_timeout(drvdata->base,
@ -160,338 +60,73 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
TMC_FFCR);
}
tmc_wait_for_ready(drvdata);
tmc_wait_for_tmcready(drvdata);
}
static void tmc_enable_hw(struct tmc_drvdata *drvdata)
void tmc_enable_hw(struct tmc_drvdata *drvdata)
{
writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL);
}
static void tmc_disable_hw(struct tmc_drvdata *drvdata)
void tmc_disable_hw(struct tmc_drvdata *drvdata)
{
writel_relaxed(0x0, drvdata->base + TMC_CTL);
}
static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
{
/* Zero out the memory to help with debug */
memset(drvdata->buf, 0, drvdata->size);
CS_UNLOCK(drvdata->base);
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
TMC_FFCR_TRIGON_TRIGIN,
drvdata->base + TMC_FFCR);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{
u32 axictl;
/* Zero out the memory to help with debug */
memset(drvdata->vaddr, 0, drvdata->size);
CS_UNLOCK(drvdata->base);
writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
axictl |= TMC_AXICTL_WR_BURST_LEN;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
axictl = (axictl &
~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
TMC_AXICTL_PROT_CTL_B1;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
TMC_FFCR_TRIGON_TRIGIN,
drvdata->base + TMC_FFCR);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
drvdata->base + TMC_FFCR);
writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
{
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_enable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_enable_hw(drvdata);
} else {
if (mode == TMC_MODE_CIRCULAR_BUFFER)
tmc_etb_enable_hw(drvdata);
else
tmc_etf_enable_hw(drvdata);
}
drvdata->enable = true;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC enabled\n");
return 0;
}
static int tmc_enable_sink(struct coresight_device *csdev, u32 mode)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
}
static int tmc_enable_link(struct coresight_device *csdev, int inport,
int outport)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO);
}
static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
{
enum tmc_mem_intf_width memwidth;
u8 memwords;
char *bufp;
u32 read_data;
int i;
memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
memwords = 1;
else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
memwords = 2;
else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
memwords = 4;
else
memwords = 8;
bufp = drvdata->buf;
while (1) {
for (i = 0; i < memwords; i++) {
read_data = readl_relaxed(drvdata->base + TMC_RRD);
if (read_data == 0xFFFFFFFF)
return;
memcpy(bufp, &read_data, 4);
bufp += 4;
}
}
}
static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
tmc_etb_dump_hw(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
{
u32 rwp, val;
rwp = readl_relaxed(drvdata->base + TMC_RWP);
val = readl_relaxed(drvdata->base + TMC_STS);
/* How much memory do we still have */
if (val & BIT(0))
drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
else
drvdata->buf = drvdata->vaddr;
}
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
tmc_etr_dump_hw(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
{
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_disable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_disable_hw(drvdata);
} else {
if (mode == TMC_MODE_CIRCULAR_BUFFER)
tmc_etb_disable_hw(drvdata);
else
tmc_etf_disable_hw(drvdata);
}
out:
drvdata->enable = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC disabled\n");
}
static void tmc_disable_sink(struct coresight_device *csdev)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
}
static void tmc_disable_link(struct coresight_device *csdev, int inport,
int outport)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO);
}
static const struct coresight_ops_sink tmc_sink_ops = {
.enable = tmc_enable_sink,
.disable = tmc_disable_sink,
};
static const struct coresight_ops_link tmc_link_ops = {
.enable = tmc_enable_link,
.disable = tmc_disable_link,
};
static const struct coresight_ops tmc_etb_cs_ops = {
.sink_ops = &tmc_sink_ops,
};
static const struct coresight_ops tmc_etr_cs_ops = {
.sink_ops = &tmc_sink_ops,
};
static const struct coresight_ops tmc_etf_cs_ops = {
.sink_ops = &tmc_sink_ops,
.link_ops = &tmc_link_ops,
};
static int tmc_read_prepare(struct tmc_drvdata *drvdata)
{
int ret;
unsigned long flags;
enum tmc_mode mode;
int ret = 0;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (!drvdata->enable)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_disable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_disable_hw(drvdata);
} else {
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode == TMC_MODE_CIRCULAR_BUFFER) {
tmc_etb_disable_hw(drvdata);
} else {
ret = -ENODEV;
goto err;
}
switch (drvdata->config_type) {
case TMC_CONFIG_TYPE_ETB:
case TMC_CONFIG_TYPE_ETF:
ret = tmc_read_prepare_etb(drvdata);
break;
case TMC_CONFIG_TYPE_ETR:
ret = tmc_read_prepare_etr(drvdata);
break;
default:
ret = -EINVAL;
}
out:
drvdata->reading = true;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC read start\n");
return 0;
err:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (!ret)
dev_info(drvdata->dev, "TMC read start\n");
return ret;
}
static void tmc_read_unprepare(struct tmc_drvdata *drvdata)
static int tmc_read_unprepare(struct tmc_drvdata *drvdata)
{
unsigned long flags;
enum tmc_mode mode;
int ret = 0;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (!drvdata->enable)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_enable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_enable_hw(drvdata);
} else {
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode == TMC_MODE_CIRCULAR_BUFFER)
tmc_etb_enable_hw(drvdata);
switch (drvdata->config_type) {
case TMC_CONFIG_TYPE_ETB:
case TMC_CONFIG_TYPE_ETF:
ret = tmc_read_unprepare_etb(drvdata);
break;
case TMC_CONFIG_TYPE_ETR:
ret = tmc_read_unprepare_etr(drvdata);
break;
default:
ret = -EINVAL;
}
out:
drvdata->reading = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC read end\n");
if (!ret)
dev_info(drvdata->dev, "TMC read end\n");
return ret;
}
static int tmc_open(struct inode *inode, struct file *file)
{
int ret;
struct tmc_drvdata *drvdata = container_of(file->private_data,
struct tmc_drvdata, miscdev);
int ret = 0;
if (drvdata->read_count++)
goto out;
ret = tmc_read_prepare(drvdata);
if (ret)
return ret;
out:
nonseekable_open(inode, file);
dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
@ -531,19 +166,14 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
static int tmc_release(struct inode *inode, struct file *file)
{
int ret;
struct tmc_drvdata *drvdata = container_of(file->private_data,
struct tmc_drvdata, miscdev);
if (--drvdata->read_count) {
if (drvdata->read_count < 0) {
dev_err(drvdata->dev, "mismatched close\n");
drvdata->read_count = 0;
}
goto out;
}
ret = tmc_read_unprepare(drvdata);
if (ret)
return ret;
tmc_read_unprepare(drvdata);
out:
dev_dbg(drvdata->dev, "%s: released\n", __func__);
return 0;
}
@ -556,56 +186,71 @@ static const struct file_operations tmc_fops = {
.llseek = no_llseek,
};
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid)
{
unsigned long flags;
u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg;
u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr;
u32 devid;
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
enum tmc_mem_intf_width memwidth;
pm_runtime_get_sync(drvdata->dev);
spin_lock_irqsave(&drvdata->spinlock, flags);
CS_UNLOCK(drvdata->base);
/*
* Excerpt from the TRM:
*
* DEVID::MEMWIDTH[10:8]
* 0x2 Memory interface databus is 32 bits wide.
* 0x3 Memory interface databus is 64 bits wide.
* 0x4 Memory interface databus is 128 bits wide.
* 0x5 Memory interface databus is 256 bits wide.
*/
switch (BMVAL(devid, 8, 10)) {
case 0x2:
memwidth = TMC_MEM_INTF_WIDTH_32BITS;
break;
case 0x3:
memwidth = TMC_MEM_INTF_WIDTH_64BITS;
break;
case 0x4:
memwidth = TMC_MEM_INTF_WIDTH_128BITS;
break;
case 0x5:
memwidth = TMC_MEM_INTF_WIDTH_256BITS;
break;
default:
memwidth = 0;
}
tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ);
tmc_sts = readl_relaxed(drvdata->base + TMC_STS);
tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP);
tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP);
tmc_trg = readl_relaxed(drvdata->base + TMC_TRG);
tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL);
tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR);
tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
tmc_mode = readl_relaxed(drvdata->base + TMC_MODE);
tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR);
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
pm_runtime_put(drvdata->dev);
return sprintf(buf,
"Depth:\t\t0x%x\n"
"Status:\t\t0x%x\n"
"RAM read ptr:\t0x%x\n"
"RAM wrt ptr:\t0x%x\n"
"Trigger cnt:\t0x%x\n"
"Control:\t0x%x\n"
"Flush status:\t0x%x\n"
"Flush ctrl:\t0x%x\n"
"Mode:\t\t0x%x\n"
"PSRC:\t\t0x%x\n"
"DEVID:\t\t0x%x\n",
tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg,
tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid);
return -EINVAL;
return memwidth;
}
static DEVICE_ATTR_RO(status);
static ssize_t trigger_cntr_show(struct device *dev,
struct device_attribute *attr, char *buf)
#define coresight_tmc_simple_func(name, offset) \
coresight_simple_func(struct tmc_drvdata, name, offset)
coresight_tmc_simple_func(rsz, TMC_RSZ);
coresight_tmc_simple_func(sts, TMC_STS);
coresight_tmc_simple_func(rrp, TMC_RRP);
coresight_tmc_simple_func(rwp, TMC_RWP);
coresight_tmc_simple_func(trg, TMC_TRG);
coresight_tmc_simple_func(ctl, TMC_CTL);
coresight_tmc_simple_func(ffsr, TMC_FFSR);
coresight_tmc_simple_func(ffcr, TMC_FFCR);
coresight_tmc_simple_func(mode, TMC_MODE);
coresight_tmc_simple_func(pscr, TMC_PSCR);
coresight_tmc_simple_func(devid, CORESIGHT_DEVID);
static struct attribute *coresight_tmc_mgmt_attrs[] = {
&dev_attr_rsz.attr,
&dev_attr_sts.attr,
&dev_attr_rrp.attr,
&dev_attr_rwp.attr,
&dev_attr_trg.attr,
&dev_attr_ctl.attr,
&dev_attr_ffsr.attr,
&dev_attr_ffcr.attr,
&dev_attr_mode.attr,
&dev_attr_pscr.attr,
&dev_attr_devid.attr,
NULL,
};
ssize_t trigger_cntr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->trigger_cntr;
@ -630,26 +275,25 @@ static ssize_t trigger_cntr_store(struct device *dev,
}
static DEVICE_ATTR_RW(trigger_cntr);
static struct attribute *coresight_etb_attrs[] = {
static struct attribute *coresight_tmc_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etb);
static struct attribute *coresight_etr_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
static const struct attribute_group coresight_tmc_group = {
.attrs = coresight_tmc_attrs,
};
ATTRIBUTE_GROUPS(coresight_etr);
static struct attribute *coresight_etf_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
static const struct attribute_group coresight_tmc_mgmt_group = {
.attrs = coresight_tmc_mgmt_attrs,
.name = "mgmt",
};
const struct attribute_group *coresight_tmc_groups[] = {
&coresight_tmc_group,
&coresight_tmc_mgmt_group,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etf);
static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
{
@ -688,6 +332,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config_type = BMVAL(devid, 6, 7);
drvdata->memwidth = tmc_get_memwidth(devid);
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
if (np)
@ -702,20 +347,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_put(&adev->dev);
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size,
&drvdata->paddr, GFP_KERNEL);
if (!drvdata->vaddr)
return -ENOMEM;
memset(drvdata->vaddr, 0, drvdata->size);
drvdata->buf = drvdata->vaddr;
} else {
drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
if (!drvdata->buf)
return -ENOMEM;
}
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc) {
ret = -ENOMEM;
@ -725,20 +356,18 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
desc->pdata = pdata;
desc->dev = dev;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc->groups = coresight_tmc_groups;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
desc->type = CORESIGHT_DEV_TYPE_SINK;
desc->ops = &tmc_etb_cs_ops;
desc->groups = coresight_etb_groups;
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
desc->type = CORESIGHT_DEV_TYPE_SINK;
desc->ops = &tmc_etr_cs_ops;
desc->groups = coresight_etr_groups;
} else {
desc->type = CORESIGHT_DEV_TYPE_LINKSINK;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
desc->ops = &tmc_etf_cs_ops;
desc->groups = coresight_etf_groups;
}
drvdata->csdev = coresight_register(desc);
@ -754,7 +383,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
if (ret)
goto err_misc_register;
dev_info(dev, "TMC initialized\n");
return 0;
err_misc_register:

View File

@ -0,0 +1,140 @@
/*
* Copyright(C) 2015 Linaro Limited. All rights reserved.
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CORESIGHT_TMC_H
#define _CORESIGHT_TMC_H
#include <linux/miscdevice.h>
#define TMC_RSZ 0x004
#define TMC_STS 0x00c
#define TMC_RRD 0x010
#define TMC_RRP 0x014
#define TMC_RWP 0x018
#define TMC_TRG 0x01c
#define TMC_CTL 0x020
#define TMC_RWD 0x024
#define TMC_MODE 0x028
#define TMC_LBUFLEVEL 0x02c
#define TMC_CBUFLEVEL 0x030
#define TMC_BUFWM 0x034
#define TMC_RRPHI 0x038
#define TMC_RWPHI 0x03c
#define TMC_AXICTL 0x110
#define TMC_DBALO 0x118
#define TMC_DBAHI 0x11c
#define TMC_FFSR 0x300
#define TMC_FFCR 0x304
#define TMC_PSCR 0x308
#define TMC_ITMISCOP0 0xee0
#define TMC_ITTRFLIN 0xee8
#define TMC_ITATBDATA0 0xeec
#define TMC_ITATBCTR2 0xef0
#define TMC_ITATBCTR1 0xef4
#define TMC_ITATBCTR0 0xef8
/* register description */
/* TMC_CTL - 0x020 */
#define TMC_CTL_CAPT_EN BIT(0)
/* TMC_STS - 0x00C */
#define TMC_STS_TMCREADY_BIT 2
#define TMC_STS_FULL BIT(0)
#define TMC_STS_TRIGGERED BIT(1)
/* TMC_AXICTL - 0x110 */
#define TMC_AXICTL_PROT_CTL_B0 BIT(0)
#define TMC_AXICTL_PROT_CTL_B1 BIT(1)
#define TMC_AXICTL_SCT_GAT_MODE BIT(7)
#define TMC_AXICTL_WR_BURST_16 0xF00
/* TMC_FFCR - 0x304 */
#define TMC_FFCR_FLUSHMAN_BIT 6
#define TMC_FFCR_EN_FMT BIT(0)
#define TMC_FFCR_EN_TI BIT(1)
#define TMC_FFCR_FON_FLIN BIT(4)
#define TMC_FFCR_FON_TRIG_EVT BIT(5)
#define TMC_FFCR_TRIGON_TRIGIN BIT(8)
#define TMC_FFCR_STOP_ON_FLUSH BIT(12)
enum tmc_config_type {
TMC_CONFIG_TYPE_ETB,
TMC_CONFIG_TYPE_ETR,
TMC_CONFIG_TYPE_ETF,
};
enum tmc_mode {
TMC_MODE_CIRCULAR_BUFFER,
TMC_MODE_SOFTWARE_FIFO,
TMC_MODE_HARDWARE_FIFO,
};
enum tmc_mem_intf_width {
TMC_MEM_INTF_WIDTH_32BITS = 1,
TMC_MEM_INTF_WIDTH_64BITS = 2,
TMC_MEM_INTF_WIDTH_128BITS = 4,
TMC_MEM_INTF_WIDTH_256BITS = 8,
};
/**
* struct tmc_drvdata - specifics associated to an TMC component
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
* @spinlock: only one at a time pls.
* @buf: area of memory where trace data get sent.
* @paddr: DMA start location in RAM.
* @vaddr: virtual representation of @paddr.
* @size: @buf size.
* @mode: how this TMC is being used.
* @config_type: TMC variant, must be of type @tmc_config_type.
* @memwidth: width of the memory interface databus, in bytes.
* @trigger_cntr: amount of words to store after a trigger.
*/
struct tmc_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct miscdevice miscdev;
spinlock_t spinlock;
bool reading;
char *buf;
dma_addr_t paddr;
void __iomem *vaddr;
u32 size;
local_t mode;
enum tmc_config_type config_type;
enum tmc_mem_intf_width memwidth;
u32 trigger_cntr;
};
/* Generic functions */
void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata);
void tmc_flush_and_stop(struct tmc_drvdata *drvdata);
void tmc_enable_hw(struct tmc_drvdata *drvdata);
void tmc_disable_hw(struct tmc_drvdata *drvdata);
/* ETB/ETF functions */
int tmc_read_prepare_etb(struct tmc_drvdata *drvdata);
int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata);
extern const struct coresight_ops tmc_etb_cs_ops;
extern const struct coresight_ops tmc_etf_cs_ops;
/* ETR functions */
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata);
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata);
extern const struct coresight_ops tmc_etr_cs_ops;
#endif

View File

@ -167,7 +167,6 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "TPIU initialized\n");
return 0;
}

View File

@ -43,7 +43,15 @@ struct coresight_node {
* When operating Coresight drivers from the sysFS interface, only a single
* path can exist from a tracer (associated to a CPU) to a sink.
*/
static DEFINE_PER_CPU(struct list_head *, sysfs_path);
static DEFINE_PER_CPU(struct list_head *, tracer_path);
/*
* As of this writing only a single STM can be found in CS topologies. Since
* there is no way to know if we'll ever see more and what kind of
* configuration they will enact, for the time being only define a single path
* for STM.
*/
static struct list_head *stm_path;
static int coresight_id_match(struct device *dev, void *data)
{
@ -257,15 +265,27 @@ static void coresight_disable_source(struct coresight_device *csdev)
void coresight_disable_path(struct list_head *path)
{
u32 type;
struct coresight_node *nd;
struct coresight_device *csdev, *parent, *child;
list_for_each_entry(nd, path, link) {
csdev = nd->csdev;
type = csdev->type;
switch (csdev->type) {
/*
* ETF devices are tricky... They can be a link or a sink,
* depending on how they are configured. If an ETF has been
* "activated" it will be configured as a sink, otherwise
* go ahead with the link configuration.
*/
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
type = (csdev == coresight_get_sink(path)) ?
CORESIGHT_DEV_TYPE_SINK :
CORESIGHT_DEV_TYPE_LINK;
switch (type) {
case CORESIGHT_DEV_TYPE_SINK:
case CORESIGHT_DEV_TYPE_LINKSINK:
coresight_disable_sink(csdev);
break;
case CORESIGHT_DEV_TYPE_SOURCE:
@ -286,15 +306,27 @@ int coresight_enable_path(struct list_head *path, u32 mode)
{
int ret = 0;
u32 type;
struct coresight_node *nd;
struct coresight_device *csdev, *parent, *child;
list_for_each_entry_reverse(nd, path, link) {
csdev = nd->csdev;
type = csdev->type;
switch (csdev->type) {
/*
* ETF devices are tricky... They can be a link or a sink,
* depending on how they are configured. If an ETF has been
* "activated" it will be configured as a sink, otherwise
* go ahead with the link configuration.
*/
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
type = (csdev == coresight_get_sink(path)) ?
CORESIGHT_DEV_TYPE_SINK :
CORESIGHT_DEV_TYPE_LINK;
switch (type) {
case CORESIGHT_DEV_TYPE_SINK:
case CORESIGHT_DEV_TYPE_LINKSINK:
ret = coresight_enable_sink(csdev, mode);
if (ret)
goto err;
@ -432,18 +464,45 @@ void coresight_release_path(struct list_head *path)
path = NULL;
}
/** coresight_validate_source - make sure a source has the right credentials
* @csdev: the device structure for a source.
* @function: the function this was called from.
*
* Assumes the coresight_mutex is held.
*/
static int coresight_validate_source(struct coresight_device *csdev,
const char *function)
{
u32 type, subtype;
type = csdev->type;
subtype = csdev->subtype.source_subtype;
if (type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", function);
return -EINVAL;
}
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) {
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
return -EINVAL;
}
return 0;
}
int coresight_enable(struct coresight_device *csdev)
{
int ret = 0;
int cpu;
int cpu, ret = 0;
struct list_head *path;
mutex_lock(&coresight_mutex);
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
ret = -EINVAL;
dev_err(&csdev->dev, "wrong device type in %s\n", __func__);
ret = coresight_validate_source(csdev, __func__);
if (ret)
goto out;
}
if (csdev->enable)
goto out;
@ -461,15 +520,25 @@ int coresight_enable(struct coresight_device *csdev)
if (ret)
goto err_source;
/*
* When working from sysFS it is important to keep track
* of the paths that were created so that they can be
* undone in 'coresight_disable()'. Since there can only
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(sysfs_path, cpu) = path;
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
/*
* When working from sysFS it is important to keep track
* of the paths that were created so that they can be
* undone in 'coresight_disable()'. Since there can only
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
stm_path = path;
break;
default:
/* We can't be here */
break;
}
out:
mutex_unlock(&coresight_mutex);
@ -486,23 +555,36 @@ EXPORT_SYMBOL_GPL(coresight_enable);
void coresight_disable(struct coresight_device *csdev)
{
int cpu;
struct list_head *path;
int cpu, ret;
struct list_head *path = NULL;
mutex_lock(&coresight_mutex);
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", __func__);
ret = coresight_validate_source(csdev, __func__);
if (ret)
goto out;
}
if (!csdev->enable)
goto out;
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(sysfs_path, cpu);
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
path = stm_path;
stm_path = NULL;
break;
default:
/* We can't be here */
break;
}
coresight_disable_source(csdev);
coresight_disable_path(path);
coresight_release_path(path);
per_cpu(sysfs_path, cpu) = NULL;
out:
mutex_unlock(&coresight_mutex);
@ -514,7 +596,7 @@ static ssize_t enable_sink_show(struct device *dev,
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->activated);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->activated);
}
static ssize_t enable_sink_store(struct device *dev,
@ -544,7 +626,7 @@ static ssize_t enable_source_show(struct device *dev,
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->enable);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->enable);
}
static ssize_t enable_source_store(struct device *dev,

View File

@ -71,6 +71,15 @@ static int intel_th_probe(struct device *dev)
if (ret)
return ret;
if (thdrv->attr_group) {
ret = sysfs_create_group(&thdev->dev.kobj, thdrv->attr_group);
if (ret) {
thdrv->remove(thdev);
return ret;
}
}
if (thdev->type == INTEL_TH_OUTPUT &&
!intel_th_output_assigned(thdev))
ret = hubdrv->assign(hub, thdev);
@ -91,6 +100,9 @@ static int intel_th_remove(struct device *dev)
return err;
}
if (thdrv->attr_group)
sysfs_remove_group(&thdev->dev.kobj, thdrv->attr_group);
thdrv->remove(thdev);
if (intel_th_output_assigned(thdev)) {
@ -171,7 +183,14 @@ static DEVICE_ATTR_RO(port);
static int intel_th_output_activate(struct intel_th_device *thdev)
{
struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
struct intel_th_driver *thdrv =
to_intel_th_driver_or_null(thdev->dev.driver);
if (!thdrv)
return -ENODEV;
if (!try_module_get(thdrv->driver.owner))
return -ENODEV;
if (thdrv->activate)
return thdrv->activate(thdev);
@ -183,12 +202,18 @@ static int intel_th_output_activate(struct intel_th_device *thdev)
static void intel_th_output_deactivate(struct intel_th_device *thdev)
{
struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
struct intel_th_driver *thdrv =
to_intel_th_driver_or_null(thdev->dev.driver);
if (!thdrv)
return;
if (thdrv->deactivate)
thdrv->deactivate(thdev);
else
intel_th_trace_disable(thdev);
module_put(thdrv->driver.owner);
}
static ssize_t active_show(struct device *dev, struct device_attribute *attr,

View File

@ -115,6 +115,7 @@ intel_th_output_assigned(struct intel_th_device *thdev)
* @enable: enable tracing for a given output device
* @disable: disable tracing for a given output device
* @fops: file operations for device nodes
* @attr_group: attributes provided by the driver
*
* Callbacks @probe and @remove are required for all device types.
* Switch device driver needs to fill in @assign, @enable and @disable
@ -139,6 +140,8 @@ struct intel_th_driver {
void (*deactivate)(struct intel_th_device *thdev);
/* file_operations for those who want a device node */
const struct file_operations *fops;
/* optional attributes */
struct attribute_group *attr_group;
/* source ops */
int (*set_output)(struct intel_th_device *thdev,
@ -148,6 +151,9 @@ struct intel_th_driver {
#define to_intel_th_driver(_d) \
container_of((_d), struct intel_th_driver, driver)
#define to_intel_th_driver_or_null(_d) \
((_d) ? to_intel_th_driver(_d) : NULL)
static inline struct intel_th_device *
to_intel_th_hub(struct intel_th_device *thdev)
{

View File

@ -122,7 +122,6 @@ struct msc {
atomic_t mmap_count;
struct mutex buf_mutex;
struct mutex iter_mutex;
struct list_head iter_list;
/* config */
@ -257,23 +256,37 @@ static struct msc_iter *msc_iter_install(struct msc *msc)
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return NULL;
return ERR_PTR(-ENOMEM);
mutex_lock(&msc->buf_mutex);
/*
* Reading and tracing are mutually exclusive; if msc is
* enabled, open() will fail; otherwise existing readers
* will prevent enabling the msc and the rest of fops don't
* need to worry about it.
*/
if (msc->enabled) {
kfree(iter);
iter = ERR_PTR(-EBUSY);
goto unlock;
}
msc_iter_init(iter);
iter->msc = msc;
mutex_lock(&msc->iter_mutex);
list_add_tail(&iter->entry, &msc->iter_list);
mutex_unlock(&msc->iter_mutex);
unlock:
mutex_unlock(&msc->buf_mutex);
return iter;
}
static void msc_iter_remove(struct msc_iter *iter, struct msc *msc)
{
mutex_lock(&msc->iter_mutex);
mutex_lock(&msc->buf_mutex);
list_del(&iter->entry);
mutex_unlock(&msc->iter_mutex);
mutex_unlock(&msc->buf_mutex);
kfree(iter);
}
@ -454,7 +467,6 @@ static void msc_buffer_clear_hw_header(struct msc *msc)
{
struct msc_window *win;
mutex_lock(&msc->buf_mutex);
list_for_each_entry(win, &msc->win_list, entry) {
unsigned int blk;
size_t hw_sz = sizeof(struct msc_block_desc) -
@ -466,7 +478,6 @@ static void msc_buffer_clear_hw_header(struct msc *msc)
memset(&bdesc->hw_tag, 0, hw_sz);
}
}
mutex_unlock(&msc->buf_mutex);
}
/**
@ -474,12 +485,15 @@ static void msc_buffer_clear_hw_header(struct msc *msc)
* @msc: the MSC device to configure
*
* Program storage mode, wrapping, burst length and trace buffer address
* into a given MSC. If msc::enabled is set, enable the trace, too.
* into a given MSC. Then, enable tracing and set msc::enabled.
* The latter is serialized on msc::buf_mutex, so make sure to hold it.
*/
static int msc_configure(struct msc *msc)
{
u32 reg;
lockdep_assert_held(&msc->buf_mutex);
if (msc->mode > MSC_MODE_MULTI)
return -ENOTSUPP;
@ -497,21 +511,19 @@ static int msc_configure(struct msc *msc)
reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL);
reg &= ~(MSC_MODE | MSC_WRAPEN | MSC_EN | MSC_RD_HDR_OVRD);
reg |= MSC_EN;
reg |= msc->mode << __ffs(MSC_MODE);
reg |= msc->burst_len << __ffs(MSC_LEN);
/*if (msc->mode == MSC_MODE_MULTI)
reg |= MSC_RD_HDR_OVRD; */
if (msc->wrap)
reg |= MSC_WRAPEN;
if (msc->enabled)
reg |= MSC_EN;
iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL);
if (msc->enabled) {
msc->thdev->output.multiblock = msc->mode == MSC_MODE_MULTI;
intel_th_trace_enable(msc->thdev);
}
msc->thdev->output.multiblock = msc->mode == MSC_MODE_MULTI;
intel_th_trace_enable(msc->thdev);
msc->enabled = 1;
return 0;
}
@ -521,15 +533,14 @@ static int msc_configure(struct msc *msc)
* @msc: MSC device to disable
*
* If @msc is enabled, disable tracing on the switch and then disable MSC
* storage.
* storage. Caller must hold msc::buf_mutex.
*/
static void msc_disable(struct msc *msc)
{
unsigned long count;
u32 reg;
if (!msc->enabled)
return;
lockdep_assert_held(&msc->buf_mutex);
intel_th_trace_disable(msc->thdev);
@ -569,33 +580,35 @@ static void msc_disable(struct msc *msc)
static int intel_th_msc_activate(struct intel_th_device *thdev)
{
struct msc *msc = dev_get_drvdata(&thdev->dev);
int ret = 0;
int ret = -EBUSY;
if (!atomic_inc_unless_negative(&msc->user_count))
return -ENODEV;
mutex_lock(&msc->iter_mutex);
if (!list_empty(&msc->iter_list))
ret = -EBUSY;
mutex_unlock(&msc->iter_mutex);
mutex_lock(&msc->buf_mutex);
if (ret) {
/* if there are readers, refuse */
if (list_empty(&msc->iter_list))
ret = msc_configure(msc);
mutex_unlock(&msc->buf_mutex);
if (ret)
atomic_dec(&msc->user_count);
return ret;
}
msc->enabled = 1;
return msc_configure(msc);
return ret;
}
static void intel_th_msc_deactivate(struct intel_th_device *thdev)
{
struct msc *msc = dev_get_drvdata(&thdev->dev);
msc_disable(msc);
atomic_dec(&msc->user_count);
mutex_lock(&msc->buf_mutex);
if (msc->enabled) {
msc_disable(msc);
atomic_dec(&msc->user_count);
}
mutex_unlock(&msc->buf_mutex);
}
/**
@ -1035,8 +1048,8 @@ static int intel_th_msc_open(struct inode *inode, struct file *file)
return -EPERM;
iter = msc_iter_install(msc);
if (!iter)
return -ENOMEM;
if (IS_ERR(iter))
return PTR_ERR(iter);
file->private_data = iter;
@ -1101,11 +1114,6 @@ static ssize_t intel_th_msc_read(struct file *file, char __user *buf,
if (!atomic_inc_unless_negative(&msc->user_count))
return 0;
if (msc->enabled) {
ret = -EBUSY;
goto put_count;
}
if (msc->mode == MSC_MODE_SINGLE && !msc->single_wrap)
size = msc->single_sz;
else
@ -1245,6 +1253,7 @@ static const struct file_operations intel_th_msc_fops = {
.read = intel_th_msc_read,
.mmap = intel_th_msc_mmap,
.llseek = no_llseek,
.owner = THIS_MODULE,
};
static int intel_th_msc_init(struct msc *msc)
@ -1254,8 +1263,6 @@ static int intel_th_msc_init(struct msc *msc)
msc->mode = MSC_MODE_MULTI;
mutex_init(&msc->buf_mutex);
INIT_LIST_HEAD(&msc->win_list);
mutex_init(&msc->iter_mutex);
INIT_LIST_HEAD(&msc->iter_list);
msc->burst_len =
@ -1393,6 +1400,11 @@ nr_pages_store(struct device *dev, struct device_attribute *attr,
do {
end = memchr(p, ',', len);
s = kstrndup(p, end ? end - p : len, GFP_KERNEL);
if (!s) {
ret = -ENOMEM;
goto free_win;
}
ret = kstrtoul(s, 10, &val);
kfree(s);
@ -1473,10 +1485,6 @@ static int intel_th_msc_probe(struct intel_th_device *thdev)
if (err)
return err;
err = sysfs_create_group(&dev->kobj, &msc_output_group);
if (err)
return err;
dev_set_drvdata(dev, msc);
return 0;
@ -1484,7 +1492,18 @@ static int intel_th_msc_probe(struct intel_th_device *thdev)
static void intel_th_msc_remove(struct intel_th_device *thdev)
{
sysfs_remove_group(&thdev->dev.kobj, &msc_output_group);
struct msc *msc = dev_get_drvdata(&thdev->dev);
int ret;
intel_th_msc_deactivate(thdev);
/*
* Buffers should not be used at this point except if the
* output character device is still open and the parent
* device gets detached from its bus, which is a FIXME.
*/
ret = msc_buffer_free_unless_used(msc);
WARN_ON_ONCE(ret);
}
static struct intel_th_driver intel_th_msc_driver = {
@ -1493,6 +1512,7 @@ static struct intel_th_driver intel_th_msc_driver = {
.activate = intel_th_msc_activate,
.deactivate = intel_th_msc_deactivate,
.fops = &intel_th_msc_fops,
.attr_group = &msc_output_group,
.driver = {
.name = "msc",
.owner = THIS_MODULE,

View File

@ -75,6 +75,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0a80),
.driver_data = (kernel_ulong_t)0,
},
{
/* Broxton B-step */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1a8e),
.driver_data = (kernel_ulong_t)0,
},
{ 0 },
};

View File

@ -200,7 +200,6 @@ static int intel_th_pti_probe(struct intel_th_device *thdev)
struct resource *res;
struct pti_device *pti;
void __iomem *base;
int ret;
res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
if (!res)
@ -219,10 +218,6 @@ static int intel_th_pti_probe(struct intel_th_device *thdev)
read_hw_config(pti);
ret = sysfs_create_group(&dev->kobj, &pti_output_group);
if (ret)
return ret;
dev_set_drvdata(dev, pti);
return 0;
@ -237,6 +232,7 @@ static struct intel_th_driver intel_th_pti_driver = {
.remove = intel_th_pti_remove,
.activate = intel_th_pti_activate,
.deactivate = intel_th_pti_deactivate,
.attr_group = &pti_output_group,
.driver = {
.name = "pti",
.owner = THIS_MODULE,

View File

@ -67,9 +67,24 @@ static ssize_t channels_show(struct device *dev,
static DEVICE_ATTR_RO(channels);
static ssize_t hw_override_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct stm_device *stm = to_stm_device(dev);
int ret;
ret = sprintf(buf, "%u\n", stm->data->hw_override);
return ret;
}
static DEVICE_ATTR_RO(hw_override);
static struct attribute *stm_attrs[] = {
&dev_attr_masters.attr,
&dev_attr_channels.attr,
&dev_attr_hw_override.attr,
NULL,
};
@ -546,8 +561,6 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg)
if (ret)
goto err_free;
ret = 0;
if (stm->data->link)
ret = stm->data->link(stm->data, stmf->output.master,
stmf->output.channel);
@ -668,6 +681,18 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
stm->dev.parent = parent;
stm->dev.release = stm_device_release;
mutex_init(&stm->link_mutex);
spin_lock_init(&stm->link_lock);
INIT_LIST_HEAD(&stm->link_list);
/* initialize the object before it is accessible via sysfs */
spin_lock_init(&stm->mc_lock);
mutex_init(&stm->policy_mutex);
stm->sw_nmasters = nmasters;
stm->owner = owner;
stm->data = stm_data;
stm_data->stm = stm;
err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name);
if (err)
goto err_device;
@ -676,20 +701,11 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
if (err)
goto err_device;
mutex_init(&stm->link_mutex);
spin_lock_init(&stm->link_lock);
INIT_LIST_HEAD(&stm->link_list);
spin_lock_init(&stm->mc_lock);
mutex_init(&stm->policy_mutex);
stm->sw_nmasters = nmasters;
stm->owner = owner;
stm->data = stm_data;
stm_data->stm = stm;
return 0;
err_device:
unregister_chrdev(stm->major, stm_data->name);
/* matches device_initialize() above */
put_device(&stm->dev);
err_free:

View File

@ -46,9 +46,7 @@ static struct stm_data dummy_stm[DUMMY_STM_MAX];
static int nr_dummies = 4;
module_param(nr_dummies, int, 0600);
static unsigned int dummy_stm_nr;
module_param(nr_dummies, int, 0400);
static unsigned int fail_mode;
@ -65,12 +63,12 @@ static int dummy_stm_link(struct stm_data *data, unsigned int master,
static int dummy_stm_init(void)
{
int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies);
int i, ret = -ENOMEM;
if (__nr_dummies < 0 || __nr_dummies > DUMMY_STM_MAX)
if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX)
return -EINVAL;
for (i = 0; i < __nr_dummies; i++) {
for (i = 0; i < nr_dummies; i++) {
dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i);
if (!dummy_stm[i].name)
goto fail_unregister;
@ -86,8 +84,6 @@ static int dummy_stm_init(void)
goto fail_free;
}
dummy_stm_nr = __nr_dummies;
return 0;
fail_unregister:
@ -105,7 +101,7 @@ static void dummy_stm_exit(void)
{
int i;
for (i = 0; i < dummy_stm_nr; i++) {
for (i = 0; i < nr_dummies; i++) {
stm_unregister_device(&dummy_stm[i]);
kfree(dummy_stm[i].name);
}

View File

@ -26,7 +26,7 @@
static int nr_devs = 4;
static int interval_ms = 10;
module_param(nr_devs, int, 0600);
module_param(nr_devs, int, 0400);
module_param(interval_ms, int, 0600);
static struct stm_heartbeat {
@ -35,8 +35,6 @@ static struct stm_heartbeat {
unsigned int active;
} stm_heartbeat[STM_HEARTBEAT_MAX];
static unsigned int nr_instances;
static const char str[] = "heartbeat stm source driver is here to serve you";
static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr)
@ -74,12 +72,12 @@ static void stm_heartbeat_unlink(struct stm_source_data *data)
static int stm_heartbeat_init(void)
{
int i, ret = -ENOMEM, __nr_instances = ACCESS_ONCE(nr_devs);
int i, ret = -ENOMEM;
if (__nr_instances < 0 || __nr_instances > STM_HEARTBEAT_MAX)
if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX)
return -EINVAL;
for (i = 0; i < __nr_instances; i++) {
for (i = 0; i < nr_devs; i++) {
stm_heartbeat[i].data.name =
kasprintf(GFP_KERNEL, "heartbeat.%d", i);
if (!stm_heartbeat[i].data.name)
@ -98,8 +96,6 @@ static int stm_heartbeat_init(void)
goto fail_free;
}
nr_instances = __nr_instances;
return 0;
fail_unregister:
@ -116,7 +112,7 @@ static void stm_heartbeat_exit(void)
{
int i;
for (i = 0; i < nr_instances; i++) {
for (i = 0; i < nr_devs; i++) {
stm_source_unregister_device(&stm_heartbeat[i].data);
kfree(stm_heartbeat[i].data.name);
}

View File

@ -107,8 +107,7 @@ stp_policy_node_masters_store(struct config_item *item, const char *page,
goto unlock;
/* must be within [sw_start..sw_end], which is an inclusive range */
if (first > INT_MAX || last > INT_MAX || first > last ||
first < stm->data->sw_start ||
if (first > last || first < stm->data->sw_start ||
last > stm->data->sw_end) {
ret = -ERANGE;
goto unlock;
@ -342,7 +341,7 @@ stp_policies_make(struct config_group *group, const char *name)
return ERR_PTR(-EINVAL);
}
*p++ = '\0';
*p = '\0';
stm = stm_find_device(devname);
kfree(devname);

View File

@ -83,13 +83,67 @@ static int mcb_remove(struct device *dev)
static void mcb_shutdown(struct device *dev)
{
struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
struct mcb_device *mdev = to_mcb_device(dev);
struct mcb_driver *mdrv = mdev->driver;
if (mdrv && mdrv->shutdown)
mdrv->shutdown(mdev);
}
static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mcb_bus *bus = to_mcb_bus(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", bus->revision);
}
static DEVICE_ATTR_RO(revision);
static ssize_t model_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mcb_bus *bus = to_mcb_bus(dev);
return scnprintf(buf, PAGE_SIZE, "%c\n", bus->model);
}
static DEVICE_ATTR_RO(model);
static ssize_t minor_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mcb_bus *bus = to_mcb_bus(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", bus->minor);
}
static DEVICE_ATTR_RO(minor);
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mcb_bus *bus = to_mcb_bus(dev);
return scnprintf(buf, PAGE_SIZE, "%s\n", bus->name);
}
static DEVICE_ATTR_RO(name);
static struct attribute *mcb_bus_attrs[] = {
&dev_attr_revision.attr,
&dev_attr_model.attr,
&dev_attr_minor.attr,
&dev_attr_name.attr,
NULL,
};
static const struct attribute_group mcb_carrier_group = {
.attrs = mcb_bus_attrs,
};
static const struct attribute_group *mcb_carrier_groups[] = {
&mcb_carrier_group,
NULL,
};
static struct bus_type mcb_bus_type = {
.name = "mcb",
.match = mcb_match,
@ -99,6 +153,11 @@ static struct bus_type mcb_bus_type = {
.shutdown = mcb_shutdown,
};
static struct device_type mcb_carrier_device_type = {
.name = "mcb-carrier",
.groups = mcb_carrier_groups,
};
/**
* __mcb_register_driver() - Register a @mcb_driver at the system
* @drv: The @mcb_driver
@ -155,6 +214,7 @@ int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
int device_id;
device_initialize(&dev->dev);
mcb_bus_get(bus);
dev->dev.bus = &mcb_bus_type;
dev->dev.parent = bus->dev.parent;
dev->dev.release = mcb_release_dev;
@ -178,6 +238,15 @@ out:
}
EXPORT_SYMBOL_GPL(mcb_device_register);
static void mcb_free_bus(struct device *dev)
{
struct mcb_bus *bus = to_mcb_bus(dev);
put_device(bus->carrier);
ida_simple_remove(&mcb_ida, bus->bus_nr);
kfree(bus);
}
/**
* mcb_alloc_bus() - Allocate a new @mcb_bus
*
@ -187,6 +256,7 @@ struct mcb_bus *mcb_alloc_bus(struct device *carrier)
{
struct mcb_bus *bus;
int bus_nr;
int rc;
bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
if (!bus)
@ -194,14 +264,29 @@ struct mcb_bus *mcb_alloc_bus(struct device *carrier)
bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
if (bus_nr < 0) {
kfree(bus);
return ERR_PTR(bus_nr);
rc = bus_nr;
goto err_free;
}
INIT_LIST_HEAD(&bus->children);
bus->bus_nr = bus_nr;
bus->carrier = carrier;
bus->carrier = get_device(carrier);
device_initialize(&bus->dev);
bus->dev.parent = carrier;
bus->dev.bus = &mcb_bus_type;
bus->dev.type = &mcb_carrier_device_type;
bus->dev.release = &mcb_free_bus;
dev_set_name(&bus->dev, "mcb:%d", bus_nr);
rc = device_add(&bus->dev);
if (rc)
goto err_free;
return bus;
err_free:
put_device(carrier);
kfree(bus);
return ERR_PTR(rc);
}
EXPORT_SYMBOL_GPL(mcb_alloc_bus);
@ -224,10 +309,6 @@ static void mcb_devices_unregister(struct mcb_bus *bus)
void mcb_release_bus(struct mcb_bus *bus)
{
mcb_devices_unregister(bus);
ida_simple_remove(&mcb_ida, bus->bus_nr);
kfree(bus);
}
EXPORT_SYMBOL_GPL(mcb_release_bus);

View File

@ -5,7 +5,6 @@
#define PCI_VENDOR_ID_MEN 0x1a88
#define PCI_DEVICE_ID_MEN_CHAMELEON 0x4d45
#define CHAMELEON_FILENAME_LEN 12
#define CHAMELEONV2_MAGIC 0xabce
#define CHAM_HEADER_SIZE 0x200

View File

@ -57,7 +57,7 @@ static int chameleon_parse_gdd(struct mcb_bus *bus,
mdev->id = GDD_DEV(reg1);
mdev->rev = GDD_REV(reg1);
mdev->var = GDD_VAR(reg1);
mdev->bar = GDD_BAR(reg1);
mdev->bar = GDD_BAR(reg2);
mdev->group = GDD_GRP(reg2);
mdev->inst = GDD_INS(reg2);
@ -113,16 +113,11 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
}
p += hsize;
pr_debug("header->revision = %d\n", header->revision);
pr_debug("header->model = 0x%x ('%c')\n", header->model,
header->model);
pr_debug("header->minor = %d\n", header->minor);
pr_debug("header->bus_type = 0x%x\n", header->bus_type);
pr_debug("header->magic = 0x%x\n", header->magic);
pr_debug("header->filename = \"%.*s\"\n", CHAMELEON_FILENAME_LEN,
header->filename);
bus->revision = header->revision;
bus->model = header->model;
bus->minor = header->minor;
snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s",
header->filename);
for_each_chameleon_cell(dtype, p) {
switch (dtype) {

View File

@ -35,7 +35,6 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct resource *res;
struct priv *priv;
int ret;
int num_cells;
unsigned long flags;
priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL);
@ -55,19 +54,20 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_disable;
}
res = request_mem_region(priv->mapbase, CHAM_HEADER_SIZE,
KBUILD_MODNAME);
res = devm_request_mem_region(&pdev->dev, priv->mapbase,
CHAM_HEADER_SIZE,
KBUILD_MODNAME);
if (!res) {
dev_err(&pdev->dev, "Failed to request PCI memory\n");
ret = -EBUSY;
goto out_disable;
}
priv->base = ioremap(priv->mapbase, CHAM_HEADER_SIZE);
priv->base = devm_ioremap(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE);
if (!priv->base) {
dev_err(&pdev->dev, "Cannot ioremap\n");
ret = -ENOMEM;
goto out_release;
goto out_disable;
}
flags = pci_resource_flags(pdev, 0);
@ -75,7 +75,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = -ENOTSUPP;
dev_err(&pdev->dev,
"IO mapped PCI devices are not supported\n");
goto out_iounmap;
goto out_disable;
}
pci_set_drvdata(pdev, priv);
@ -83,7 +83,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
priv->bus = mcb_alloc_bus(&pdev->dev);
if (IS_ERR(priv->bus)) {
ret = PTR_ERR(priv->bus);
goto out_iounmap;
goto out_disable;
}
priv->bus->get_irq = mcb_pci_get_irq;
@ -91,9 +91,8 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = chameleon_parse_cells(priv->bus, priv->mapbase, priv->base);
if (ret < 0)
goto out_mcb_bus;
num_cells = ret;
dev_dbg(&pdev->dev, "Found %d cells\n", num_cells);
dev_dbg(&pdev->dev, "Found %d cells\n", ret);
mcb_bus_add_devices(priv->bus);
@ -101,10 +100,6 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
out_mcb_bus:
mcb_release_bus(priv->bus);
out_iounmap:
iounmap(priv->base);
out_release:
pci_release_region(pdev, 0);
out_disable:
pci_disable_device(pdev);
return ret;
@ -116,8 +111,6 @@ static void mcb_pci_remove(struct pci_dev *pdev)
mcb_release_bus(priv->bus);
iounmap(priv->base);
release_region(priv->mapbase, CHAM_HEADER_SIZE);
pci_disable_device(pdev);
}

View File

@ -109,7 +109,7 @@ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
struct lpddr2_timings *timings = NULL;
u32 arr_sz = 0, i = 0;
struct device_node *np_tim;
char *tim_compat;
char *tim_compat = NULL;
switch (device_type) {
case DDR_TYPE_LPDDR2_S2:

View File

@ -3,7 +3,6 @@ menu "EEPROM support"
config EEPROM_AT24
tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
depends on I2C && SYSFS
select REGMAP
select NVMEM
help
Enable this driver to get read/write support to most I2C EEPROMs
@ -32,7 +31,6 @@ config EEPROM_AT24
config EEPROM_AT25
tristate "SPI EEPROMs from most vendors"
depends on SPI && SYSFS
select REGMAP
select NVMEM
help
Enable this driver to get read/write support to most SPI EEPROMs,

View File

@ -23,7 +23,6 @@
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/platform_data/at24.h>
/*
@ -69,7 +68,6 @@ struct at24_data {
unsigned write_max;
unsigned num_addresses;
struct regmap_config regmap_config;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
@ -251,10 +249,10 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
return -ETIMEDOUT;
}
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
static int at24_read(void *priv, unsigned int off, void *val, size_t count)
{
ssize_t retval = 0;
struct at24_data *at24 = priv;
char *buf = val;
if (unlikely(!count))
return count;
@ -266,23 +264,21 @@ static ssize_t at24_read(struct at24_data *at24,
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
int status;
status = at24_eeprom_read(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
if (status < 0) {
mutex_unlock(&at24->lock);
return status;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
return 0;
}
/*
@ -370,13 +366,13 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
return -ETIMEDOUT;
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
static int at24_write(void *priv, unsigned int off, void *val, size_t count)
{
ssize_t retval = 0;
struct at24_data *at24 = priv;
char *buf = val;
if (unlikely(!count))
return count;
return -EINVAL;
/*
* Write data to chip, protecting against concurrent updates
@ -385,70 +381,23 @@ static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
int status;
status = at24_eeprom_write(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
if (status < 0) {
mutex_unlock(&at24->lock);
return status;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
/*-------------------------------------------------------------------------*/
/*
* Provide a regmap interface, which is registered with the NVMEM
* framework
*/
static int at24_regmap_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct at24_data *at24 = context;
off_t offset = *(u32 *)reg;
int err;
err = at24_read(at24, val, offset, val_size);
if (err)
return err;
return 0;
}
static int at24_regmap_write(void *context, const void *data, size_t count)
{
struct at24_data *at24 = context;
const char *buf;
u32 offset;
size_t len;
int err;
memcpy(&offset, data, sizeof(offset));
buf = (const char *)data + sizeof(offset);
len = count - sizeof(offset);
err = at24_write(at24, buf, offset, len);
if (err)
return err;
return 0;
}
static const struct regmap_bus at24_regmap_bus = {
.read = at24_regmap_read,
.write = at24_regmap_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
@ -480,7 +429,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
struct at24_data *at24;
int err;
unsigned i, num_addresses;
struct regmap *regmap;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
@ -607,19 +555,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
}
at24->regmap_config.reg_bits = 32;
at24->regmap_config.val_bits = 8;
at24->regmap_config.reg_stride = 1;
at24->regmap_config.max_register = chip.byte_len - 1;
regmap = devm_regmap_init(&client->dev, &at24_regmap_bus, at24,
&at24->regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "regmap init failed\n");
err = PTR_ERR(regmap);
goto err_clients;
}
at24->nvmem_config.name = dev_name(&client->dev);
at24->nvmem_config.dev = &client->dev;
at24->nvmem_config.read_only = !writable;
@ -627,6 +562,12 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->nvmem_config.owner = THIS_MODULE;
at24->nvmem_config.compat = true;
at24->nvmem_config.base_dev = &client->dev;
at24->nvmem_config.reg_read = at24_read;
at24->nvmem_config.reg_write = at24_write;
at24->nvmem_config.priv = at24;
at24->nvmem_config.stride = 4;
at24->nvmem_config.word_size = 1;
at24->nvmem_config.size = chip.byte_len;
at24->nvmem = nvmem_register(&at24->nvmem_config);

View File

@ -17,7 +17,6 @@
#include <linux/sched.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
#include <linux/property.h>
@ -34,7 +33,6 @@ struct at25_data {
struct mutex lock;
struct spi_eeprom chip;
unsigned addrlen;
struct regmap_config regmap_config;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
};
@ -65,14 +63,11 @@ struct at25_data {
#define io_limit PAGE_SIZE /* bytes */
static ssize_t
at25_ee_read(
struct at25_data *at25,
char *buf,
unsigned offset,
size_t count
)
static int at25_ee_read(void *priv, unsigned int offset,
void *val, size_t count)
{
struct at25_data *at25 = priv;
char *buf = val;
u8 command[EE_MAXADDRLEN + 1];
u8 *cp;
ssize_t status;
@ -81,11 +76,11 @@ at25_ee_read(
u8 instr;
if (unlikely(offset >= at25->chip.byte_len))
return 0;
return -EINVAL;
if ((offset + count) > at25->chip.byte_len)
count = at25->chip.byte_len - offset;
if (unlikely(!count))
return count;
return -EINVAL;
cp = command;
@ -131,28 +126,14 @@ at25_ee_read(
count, offset, (int) status);
mutex_unlock(&at25->lock);
return status ? status : count;
return status;
}
static int at25_regmap_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
{
struct at25_data *at25 = context;
off_t offset = *(u32 *)reg;
int err;
err = at25_ee_read(at25, val, offset, val_size);
if (err)
return err;
return 0;
}
static ssize_t
at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
size_t count)
{
ssize_t status = 0;
unsigned written = 0;
struct at25_data *at25 = priv;
const char *buf = val;
int status = 0;
unsigned buf_size;
u8 *bounce;
@ -161,7 +142,7 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
if ((off + count) > at25->chip.byte_len)
count = at25->chip.byte_len - off;
if (unlikely(!count))
return count;
return -EINVAL;
/* Temp buffer starts with command and address */
buf_size = at25->chip.page_size;
@ -256,40 +237,15 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
off += segment;
buf += segment;
count -= segment;
written += segment;
} while (count > 0);
mutex_unlock(&at25->lock);
kfree(bounce);
return written ? written : status;
return status;
}
static int at25_regmap_write(void *context, const void *data, size_t count)
{
struct at25_data *at25 = context;
const char *buf;
u32 offset;
size_t len;
int err;
memcpy(&offset, data, sizeof(offset));
buf = (const char *)data + sizeof(offset);
len = count - sizeof(offset);
err = at25_ee_write(at25, buf, offset, len);
if (err)
return err;
return 0;
}
static const struct regmap_bus at25_regmap_bus = {
.read = at25_regmap_read,
.write = at25_regmap_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
/*-------------------------------------------------------------------------*/
static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
@ -349,7 +305,6 @@ static int at25_probe(struct spi_device *spi)
{
struct at25_data *at25 = NULL;
struct spi_eeprom chip;
struct regmap *regmap;
int err;
int sr;
int addrlen;
@ -390,22 +345,10 @@ static int at25_probe(struct spi_device *spi)
mutex_init(&at25->lock);
at25->chip = chip;
at25->spi = spi_dev_get(spi);
at25->spi = spi;
spi_set_drvdata(spi, at25);
at25->addrlen = addrlen;
at25->regmap_config.reg_bits = 32;
at25->regmap_config.val_bits = 8;
at25->regmap_config.reg_stride = 1;
at25->regmap_config.max_register = chip.byte_len - 1;
regmap = devm_regmap_init(&spi->dev, &at25_regmap_bus, at25,
&at25->regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "regmap init failed\n");
return PTR_ERR(regmap);
}
at25->nvmem_config.name = dev_name(&spi->dev);
at25->nvmem_config.dev = &spi->dev;
at25->nvmem_config.read_only = chip.flags & EE_READONLY;
@ -413,6 +356,12 @@ static int at25_probe(struct spi_device *spi)
at25->nvmem_config.owner = THIS_MODULE;
at25->nvmem_config.compat = true;
at25->nvmem_config.base_dev = &spi->dev;
at25->nvmem_config.reg_read = at25_ee_read;
at25->nvmem_config.reg_write = at25_ee_write;
at25->nvmem_config.priv = at25;
at25->nvmem_config.stride = 4;
at25->nvmem_config.word_size = 1;
at25->nvmem_config.size = chip.byte_len;
at25->nvmem = nvmem_register(&at25->nvmem_config);
if (IS_ERR(at25->nvmem))

View File

@ -20,7 +20,6 @@
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/eeprom_93xx46.h>
#define OP_START 0x4
@ -43,7 +42,6 @@ struct eeprom_93xx46_dev {
struct spi_device *spi;
struct eeprom_93xx46_platform_data *pdata;
struct mutex lock;
struct regmap_config regmap_config;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
int addrlen;
@ -60,11 +58,12 @@ static inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev)
return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH;
}
static ssize_t
eeprom_93xx46_read(struct eeprom_93xx46_dev *edev, char *buf,
unsigned off, size_t count)
static int eeprom_93xx46_read(void *priv, unsigned int off,
void *val, size_t count)
{
ssize_t ret = 0;
struct eeprom_93xx46_dev *edev = priv;
char *buf = val;
int err = 0;
if (unlikely(off >= edev->size))
return 0;
@ -84,7 +83,6 @@ eeprom_93xx46_read(struct eeprom_93xx46_dev *edev, char *buf,
u16 cmd_addr = OP_READ << edev->addrlen;
size_t nbytes = count;
int bits;
int err;
if (edev->addrlen == 7) {
cmd_addr |= off & 0x7f;
@ -120,21 +118,20 @@ eeprom_93xx46_read(struct eeprom_93xx46_dev *edev, char *buf,
if (err) {
dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
nbytes, (int)off, err);
ret = err;
break;
}
buf += nbytes;
off += nbytes;
count -= nbytes;
ret += nbytes;
}
if (edev->pdata->finish)
edev->pdata->finish(edev);
mutex_unlock(&edev->lock);
return ret;
return err;
}
static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
@ -230,10 +227,11 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
return ret;
}
static ssize_t
eeprom_93xx46_write(struct eeprom_93xx46_dev *edev, const char *buf,
loff_t off, size_t count)
static int eeprom_93xx46_write(void *priv, unsigned int off,
void *val, size_t count)
{
struct eeprom_93xx46_dev *edev = priv;
char *buf = val;
int i, ret, step = 1;
if (unlikely(off >= edev->size))
@ -275,52 +273,9 @@ eeprom_93xx46_write(struct eeprom_93xx46_dev *edev, const char *buf,
/* erase/write disable */
eeprom_93xx46_ew(edev, 0);
return ret ? : count;
return ret;
}
/*
* Provide a regmap interface, which is registered with the NVMEM
* framework
*/
static int eeprom_93xx46_regmap_read(void *context, const void *reg,
size_t reg_size, void *val,
size_t val_size)
{
struct eeprom_93xx46_dev *eeprom_93xx46 = context;
off_t offset = *(u32 *)reg;
int err;
err = eeprom_93xx46_read(eeprom_93xx46, val, offset, val_size);
if (err)
return err;
return 0;
}
static int eeprom_93xx46_regmap_write(void *context, const void *data,
size_t count)
{
struct eeprom_93xx46_dev *eeprom_93xx46 = context;
const char *buf;
u32 offset;
size_t len;
int err;
memcpy(&offset, data, sizeof(offset));
buf = (const char *)data + sizeof(offset);
len = count - sizeof(offset);
err = eeprom_93xx46_write(eeprom_93xx46, buf, offset, len);
if (err)
return err;
return 0;
}
static const struct regmap_bus eeprom_93xx46_regmap_bus = {
.read = eeprom_93xx46_regmap_read,
.write = eeprom_93xx46_regmap_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
{
struct eeprom_93xx46_platform_data *pd = edev->pdata;
@ -480,7 +435,6 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
{
struct eeprom_93xx46_platform_data *pd;
struct eeprom_93xx46_dev *edev;
struct regmap *regmap;
int err;
if (spi->dev.of_node) {
@ -511,24 +465,10 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
mutex_init(&edev->lock);
edev->spi = spi_dev_get(spi);
edev->spi = spi;
edev->pdata = pd;
edev->size = 128;
edev->regmap_config.reg_bits = 32;
edev->regmap_config.val_bits = 8;
edev->regmap_config.reg_stride = 1;
edev->regmap_config.max_register = edev->size - 1;
regmap = devm_regmap_init(&spi->dev, &eeprom_93xx46_regmap_bus, edev,
&edev->regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "regmap init failed\n");
err = PTR_ERR(regmap);
goto fail;
}
edev->nvmem_config.name = dev_name(&spi->dev);
edev->nvmem_config.dev = &spi->dev;
edev->nvmem_config.read_only = pd->flags & EE_READONLY;
@ -536,6 +476,12 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
edev->nvmem_config.owner = THIS_MODULE;
edev->nvmem_config.compat = true;
edev->nvmem_config.base_dev = &spi->dev;
edev->nvmem_config.reg_read = eeprom_93xx46_read;
edev->nvmem_config.reg_write = eeprom_93xx46_write;
edev->nvmem_config.priv = edev;
edev->nvmem_config.stride = 4;
edev->nvmem_config.word_size = 1;
edev->nvmem_config.size = edev->size;
edev->nvmem = nvmem_register(&edev->nvmem_config);
if (IS_ERR(edev->nvmem)) {

View File

@ -380,8 +380,10 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl,
dev = cl->dev;
if (dev->iamthif_state != MEI_IAMTHIF_READING)
if (dev->iamthif_state != MEI_IAMTHIF_READING) {
mei_irq_discard_msg(dev, mei_hdr);
return 0;
}
ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
if (ret)

View File

@ -220,17 +220,23 @@ EXPORT_SYMBOL_GPL(mei_cldev_recv);
static void mei_cl_bus_event_work(struct work_struct *work)
{
struct mei_cl_device *cldev;
struct mei_device *bus;
cldev = container_of(work, struct mei_cl_device, event_work);
bus = cldev->bus;
if (cldev->event_cb)
cldev->event_cb(cldev, cldev->events, cldev->event_context);
cldev->events = 0;
/* Prepare for the next read */
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX))
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
mutex_lock(&bus->device_lock);
mei_cl_read_start(cldev->cl, 0, NULL);
mutex_unlock(&bus->device_lock);
}
}
/**
@ -304,6 +310,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
unsigned long events_mask,
mei_cldev_event_cb_t event_cb, void *context)
{
struct mei_device *bus = cldev->bus;
int ret;
if (cldev->event_cb)
@ -316,15 +323,17 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
INIT_WORK(&cldev->event_work, mei_cl_bus_event_work);
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
mutex_lock(&bus->device_lock);
ret = mei_cl_read_start(cldev->cl, 0, NULL);
mutex_unlock(&bus->device_lock);
if (ret && ret != -EBUSY)
return ret;
}
if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) {
mutex_lock(&cldev->cl->dev->device_lock);
mutex_lock(&bus->device_lock);
ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0);
mutex_unlock(&cldev->cl->dev->device_lock);
mutex_unlock(&bus->device_lock);
if (ret)
return ret;
}
@ -580,6 +589,7 @@ static int mei_cl_device_probe(struct device *dev)
struct mei_cl_device *cldev;
struct mei_cl_driver *cldrv;
const struct mei_cl_device_id *id;
int ret;
cldev = to_mei_cl_device(dev);
cldrv = to_mei_cl_driver(dev->driver);
@ -594,9 +604,12 @@ static int mei_cl_device_probe(struct device *dev)
if (!id)
return -ENODEV;
__module_get(THIS_MODULE);
ret = cldrv->probe(cldev, id);
if (ret)
return ret;
return cldrv->probe(cldev, id);
__module_get(THIS_MODULE);
return 0;
}
/**
@ -634,11 +647,8 @@ static ssize_t name_show(struct device *dev, struct device_attribute *a,
char *buf)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
size_t len;
len = snprintf(buf, PAGE_SIZE, "%s", cldev->name);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
return scnprintf(buf, PAGE_SIZE, "%s", cldev->name);
}
static DEVICE_ATTR_RO(name);
@ -647,11 +657,8 @@ 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);
size_t len;
len = snprintf(buf, PAGE_SIZE, "%pUl", uuid);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
return scnprintf(buf, PAGE_SIZE, "%pUl", uuid);
}
static DEVICE_ATTR_RO(uuid);
@ -660,11 +667,8 @@ 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);
size_t len;
len = snprintf(buf, PAGE_SIZE, "%02X", version);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
return scnprintf(buf, PAGE_SIZE, "%02X", version);
}
static DEVICE_ATTR_RO(version);
@ -673,10 +677,8 @@ static ssize_t modalias_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);
size_t len;
len = snprintf(buf, PAGE_SIZE, "mei:%s:%pUl:", cldev->name, uuid);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:", cldev->name, uuid);
}
static DEVICE_ATTR_RO(modalias);

View File

@ -727,6 +727,11 @@ static void mei_cl_wake_all(struct mei_cl *cl)
cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
wake_up_interruptible(&cl->ev_wait);
}
/* synchronized under device mutex */
if (waitqueue_active(&cl->wait)) {
cl_dbg(dev, cl, "Waking up ctrl write clients!\n");
wake_up_interruptible(&cl->wait);
}
}
/**
@ -879,12 +884,15 @@ static int __mei_cl_disconnect(struct mei_cl *cl)
}
mutex_unlock(&dev->device_lock);
wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY,
wait_event_timeout(cl->wait,
cl->state == MEI_FILE_DISCONNECT_REPLY ||
cl->state == MEI_FILE_DISCONNECTED,
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
mutex_lock(&dev->device_lock);
rets = cl->status;
if (cl->state != MEI_FILE_DISCONNECT_REPLY) {
if (cl->state != MEI_FILE_DISCONNECT_REPLY &&
cl->state != MEI_FILE_DISCONNECTED) {
cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
rets = -ETIME;
}
@ -1085,6 +1093,7 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
mutex_unlock(&dev->device_lock);
wait_event_timeout(cl->wait,
(cl->state == MEI_FILE_CONNECTED ||
cl->state == MEI_FILE_DISCONNECTED ||
cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
cl->state == MEI_FILE_DISCONNECT_REPLY),
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
@ -1333,16 +1342,13 @@ int mei_cl_notify_request(struct mei_cl *cl,
}
mutex_unlock(&dev->device_lock);
wait_event_timeout(cl->wait, cl->notify_en == request,
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
wait_event_timeout(cl->wait,
cl->notify_en == request || !mei_cl_is_connected(cl),
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
mutex_lock(&dev->device_lock);
if (cl->notify_en != request) {
mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl);
if (!cl->status)
cl->status = -EFAULT;
}
if (cl->notify_en != request && !cl->status)
cl->status = -EFAULT;
rets = cl->status;
@ -1766,6 +1772,10 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
if (waitqueue_active(&cl->wait))
wake_up(&cl->wait);
break;
case MEI_FOP_DISCONNECT_RSP:
mei_io_cb_free(cb);
mei_cl_set_disconnected(cl);
break;
default:
BUG_ON(0);

View File

@ -113,8 +113,6 @@ void mei_hbm_idle(struct mei_device *dev)
*/
void mei_hbm_reset(struct mei_device *dev)
{
dev->me_client_index = 0;
mei_me_cl_rm_all(dev);
mei_hbm_idle(dev);
@ -530,24 +528,22 @@ static void mei_hbm_cl_notify(struct mei_device *dev,
* mei_hbm_prop_req - request property for a single client
*
* @dev: the device structure
* @start_idx: client index to start search
*
* Return: 0 on success and < 0 on failure
*/
static int mei_hbm_prop_req(struct mei_device *dev)
static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx)
{
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
struct hbm_props_request *prop_req;
const size_t len = sizeof(struct hbm_props_request);
unsigned long next_client_index;
unsigned long addr;
int ret;
next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
dev->me_client_index);
addr = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, start_idx);
/* We got all client properties */
if (next_client_index == MEI_CLIENTS_MAX) {
if (addr == MEI_CLIENTS_MAX) {
dev->hbm_state = MEI_HBM_STARTED;
mei_host_client_init(dev);
@ -560,7 +556,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
memset(prop_req, 0, sizeof(struct hbm_props_request));
prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
prop_req->me_addr = next_client_index;
prop_req->me_addr = addr;
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
if (ret) {
@ -570,7 +566,6 @@ static int mei_hbm_prop_req(struct mei_device *dev)
}
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
dev->me_client_index = next_client_index;
return 0;
}
@ -882,8 +877,7 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
if (!cb)
return -ENOMEM;
cl_dbg(dev, cl, "add disconnect response as first\n");
list_add(&cb->list, &dev->ctrl_wr_list.list);
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
}
return 0;
}
@ -1152,10 +1146,8 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
mei_hbm_me_cl_add(dev, props_res);
dev->me_client_index++;
/* request property for the next client */
if (mei_hbm_prop_req(dev))
if (mei_hbm_prop_req(dev, props_res->me_addr + 1))
return -EIO;
break;
@ -1181,7 +1173,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
/* first property request */
if (mei_hbm_prop_req(dev))
if (mei_hbm_prop_req(dev, 0))
return -EIO;
break;

View File

@ -76,7 +76,6 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
* @dev: mei device
* @hdr: message header
*/
static inline
void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
{
/*
@ -194,10 +193,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
return -EMSGSIZE;
ret = mei_hbm_cl_disconnect_rsp(dev, cl);
mei_cl_set_disconnected(cl);
mei_io_cb_free(cb);
mei_me_cl_put(cl->me_cl);
cl->me_cl = NULL;
list_move_tail(&cb->list, &cmpl_list->list);
return ret;
}

View File

@ -396,7 +396,6 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @me_clients : list of FW clients
* @me_clients_map : FW clients bit map
* @host_clients_map : host clients id pool
* @me_client_index : last FW client index in enumeration
*
* @allow_fixed_address: allow user space to connect a fixed client
* @override_fixed_address: force allow fixed address behavior
@ -486,7 +485,6 @@ struct mei_device {
struct list_head me_clients;
DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX);
DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX);
unsigned long me_client_index;
bool allow_fixed_address;
bool override_fixed_address;
@ -704,6 +702,8 @@ bool mei_hbuf_acquire(struct mei_device *dev);
bool mei_write_is_idle(struct mei_device *dev);
void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr);
#if IS_ENABLED(CONFIG_DEBUG_FS)
int mei_dbgfs_register(struct mei_device *dev, const char *name);
void mei_dbgfs_deregister(struct mei_device *dev);

View File

@ -132,6 +132,7 @@ config VOP
tristate "VOP Driver"
depends on 64BIT && PCI && X86 && VOP_BUS
select VHOST_RING
select VIRTIO
help
This enables VOP (Virtio over PCIe) Driver support for the Intel
Many Integrated Core (MIC) family of PCIe form factor coprocessor

View File

@ -76,7 +76,7 @@ static void __mic_free_irq(struct vop_device *vpdev,
{
struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev);
return mic_free_irq(mdev, cookie, data);
mic_free_irq(mdev, cookie, data);
}
static void __mic_ack_interrupt(struct vop_device *vpdev, int num)
@ -272,7 +272,7 @@ ___mic_free_irq(struct scif_hw_dev *scdev,
{
struct mic_device *mdev = scdev_to_mdev(scdev);
return mic_free_irq(mdev, cookie, data);
mic_free_irq(mdev, cookie, data);
}
static void ___mic_ack_interrupt(struct scif_hw_dev *scdev, int num)
@ -362,7 +362,7 @@ _mic_request_threaded_irq(struct mbus_device *mbdev,
static void _mic_free_irq(struct mbus_device *mbdev,
struct mic_irq *cookie, void *data)
{
return mic_free_irq(mbdev_to_mdev(mbdev), cookie, data);
mic_free_irq(mbdev_to_mdev(mbdev), cookie, data);
}
static void _mic_ack_interrupt(struct mbus_device *mbdev, int num)

View File

@ -27,7 +27,8 @@
void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
int mark, err;
int mark = 0;
int err;
err = _scif_fence_mark(ep, &mark);
if (err)

View File

@ -94,7 +94,8 @@ static int qcom_coincell_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct qcom_coincell chgr;
u32 rset, vset;
u32 rset = 0;
u32 vset = 0;
bool enable;
int rc;

View File

@ -364,8 +364,8 @@ static int sram_probe(struct platform_device *pdev)
sram->virt_base = devm_ioremap(sram->dev, res->start, size);
else
sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
if (IS_ERR(sram->virt_base))
return PTR_ERR(sram->virt_base);
if (!sram->virt_base)
return -ENOMEM;
sram->pool = devm_gen_pool_create(sram->dev, ilog2(SRAM_GRANULARITY),
NUMA_NO_NODE, NULL);

View File

@ -78,7 +78,6 @@ static void validate_firmware_response(struct kim_data_s *kim_gdata)
memcpy(kim_gdata->resp_buffer,
kim_gdata->rx_skb->data,
kim_gdata->rx_skb->len);
complete_all(&kim_gdata->kim_rcvd);
kim_gdata->rx_state = ST_W4_PACKET_TYPE;
kim_gdata->rx_skb = NULL;
kim_gdata->rx_count = 0;

View File

@ -1,6 +1,5 @@
menuconfig NVMEM
tristate "NVMEM Support"
select REGMAP
help
Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
@ -28,6 +27,7 @@ config NVMEM_IMX_OCOTP
config NVMEM_LPC18XX_EEPROM
tristate "NXP LPC18XX EEPROM Memory Support"
depends on ARCH_LPC18XX || COMPILE_TEST
depends on HAS_IOMEM
help
Say Y here to include support for NXP LPC18xx EEPROM memory found in
NXP LPC185x/3x and LPC435x/3x/2x/1x devices.
@ -49,6 +49,7 @@ config NVMEM_MXS_OCOTP
config MTK_EFUSE
tristate "Mediatek SoCs EFUSE support"
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on HAS_IOMEM
select REGMAP_MMIO
help
This is a driver to access hardware related data like sensor
@ -61,7 +62,6 @@ config QCOM_QFPROM
tristate "QCOM QFPROM Support"
depends on ARCH_QCOM || COMPILE_TEST
depends on HAS_IOMEM
select REGMAP_MMIO
help
Say y here to enable QFPROM support. The QFPROM provides access
functions for QFPROM data to rest of the drivers via nvmem interface.
@ -83,7 +83,6 @@ config ROCKCHIP_EFUSE
config NVMEM_SUNXI_SID
tristate "Allwinner SoCs SID support"
depends on ARCH_SUNXI
select REGMAP_MMIO
help
This is a driver for the 'security ID' available on various Allwinner
devices.

View File

@ -23,12 +23,10 @@
#include <linux/nvmem-consumer.h>
#include <linux/nvmem-provider.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
struct nvmem_device {
const char *name;
struct regmap *regmap;
struct module *owner;
struct device dev;
int stride;
@ -41,6 +39,9 @@ struct nvmem_device {
int flags;
struct bin_attribute eeprom;
struct device *base_dev;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
void *priv;
};
#define FLAG_COMPAT BIT(0)
@ -66,6 +67,23 @@ static struct lock_class_key eeprom_lock_key;
#endif
#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
if (nvmem->reg_read)
return nvmem->reg_read(nvmem->priv, offset, val, bytes);
return -EINVAL;
}
static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
if (nvmem->reg_write)
return nvmem->reg_write(nvmem->priv, offset, val, bytes);
return -EINVAL;
}
static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
@ -93,7 +111,7 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
count = round_down(count, nvmem->word_size);
rc = regmap_raw_read(nvmem->regmap, pos, buf, count);
rc = nvmem_reg_read(nvmem, pos, buf, count);
if (IS_ERR_VALUE(rc))
return rc;
@ -127,7 +145,7 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
count = round_down(count, nvmem->word_size);
rc = regmap_raw_write(nvmem->regmap, pos, buf, count);
rc = nvmem_reg_write(nvmem, pos, buf, count);
if (IS_ERR_VALUE(rc))
return rc;
@ -421,18 +439,11 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
{
struct nvmem_device *nvmem;
struct device_node *np;
struct regmap *rm;
int rval;
if (!config->dev)
return ERR_PTR(-EINVAL);
rm = dev_get_regmap(config->dev, NULL);
if (!rm) {
dev_err(config->dev, "Regmap not found\n");
return ERR_PTR(-EINVAL);
}
nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL);
if (!nvmem)
return ERR_PTR(-ENOMEM);
@ -444,14 +455,16 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
}
nvmem->id = rval;
nvmem->regmap = rm;
nvmem->owner = config->owner;
nvmem->stride = regmap_get_reg_stride(rm);
nvmem->word_size = regmap_get_val_bytes(rm);
nvmem->size = regmap_get_max_register(rm) + nvmem->stride;
nvmem->stride = config->stride;
nvmem->word_size = config->word_size;
nvmem->size = config->size;
nvmem->dev.type = &nvmem_provider_type;
nvmem->dev.bus = &nvmem_bus_type;
nvmem->dev.parent = config->dev;
nvmem->priv = config->priv;
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
np = config->dev->of_node;
nvmem->dev.of_node = np;
dev_set_name(&nvmem->dev, "%s%d",
@ -948,7 +961,7 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
{
int rc;
rc = regmap_raw_read(nvmem->regmap, cell->offset, buf, cell->bytes);
rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
if (IS_ERR_VALUE(rc))
return rc;
@ -977,7 +990,7 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
u8 *buf;
int rc;
if (!nvmem || !nvmem->regmap)
if (!nvmem)
return ERR_PTR(-EINVAL);
buf = kzalloc(cell->bytes, GFP_KERNEL);
@ -1014,7 +1027,7 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
*b <<= bit_offset;
/* setup the first byte with lsb bits from nvmem */
rc = regmap_raw_read(nvmem->regmap, cell->offset, &v, 1);
rc = nvmem_reg_read(nvmem, cell->offset, &v, 1);
*b++ |= GENMASK(bit_offset - 1, 0) & v;
/* setup rest of the byte if any */
@ -1031,7 +1044,7 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
/* if it's not end on byte boundary */
if ((nbits + bit_offset) % BITS_PER_BYTE) {
/* setup the last byte with msb bits from nvmem */
rc = regmap_raw_read(nvmem->regmap,
rc = nvmem_reg_read(nvmem,
cell->offset + cell->bytes - 1, &v, 1);
*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
@ -1054,7 +1067,7 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
struct nvmem_device *nvmem = cell->nvmem;
int rc;
if (!nvmem || !nvmem->regmap || nvmem->read_only ||
if (!nvmem || nvmem->read_only ||
(cell->bit_offset == 0 && len != cell->bytes))
return -EINVAL;
@ -1064,7 +1077,7 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
return PTR_ERR(buf);
}
rc = regmap_raw_write(nvmem->regmap, cell->offset, buf, cell->bytes);
rc = nvmem_reg_write(nvmem, cell->offset, buf, cell->bytes);
/* free the tmp buffer */
if (cell->bit_offset || cell->nbits)
@ -1094,7 +1107,7 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
int rc;
ssize_t len;
if (!nvmem || !nvmem->regmap)
if (!nvmem)
return -EINVAL;
rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
@ -1124,7 +1137,7 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem,
struct nvmem_cell cell;
int rc;
if (!nvmem || !nvmem->regmap)
if (!nvmem)
return -EINVAL;
rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
@ -1152,10 +1165,10 @@ int nvmem_device_read(struct nvmem_device *nvmem,
{
int rc;
if (!nvmem || !nvmem->regmap)
if (!nvmem)
return -EINVAL;
rc = regmap_raw_read(nvmem->regmap, offset, buf, bytes);
rc = nvmem_reg_read(nvmem, offset, buf, bytes);
if (IS_ERR_VALUE(rc))
return rc;
@ -1180,10 +1193,10 @@ int nvmem_device_write(struct nvmem_device *nvmem,
{
int rc;
if (!nvmem || !nvmem->regmap)
if (!nvmem)
return -EINVAL;
rc = regmap_raw_write(nvmem->regmap, offset, buf, bytes);
rc = nvmem_reg_write(nvmem, offset, buf, bytes);
if (IS_ERR_VALUE(rc))
return rc;

View File

@ -22,7 +22,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
struct ocotp_priv {
@ -31,59 +30,34 @@ struct ocotp_priv {
unsigned int nregs;
};
static int imx_ocotp_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
static int imx_ocotp_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
struct ocotp_priv *priv = context;
unsigned int offset = *(u32 *)reg;
unsigned int count;
u32 *buf = val;
int i;
u32 index;
index = offset >> 2;
count = val_size >> 2;
count = bytes >> 2;
if (count > (priv->nregs - index))
count = priv->nregs - index;
for (i = index; i < (index + count); i++) {
*(u32 *)val = readl(priv->base + 0x400 + i * 0x10);
val += 4;
}
for (i = index; i < (index + count); i++)
*buf++ = readl(priv->base + 0x400 + i * 0x10);
return 0;
}
static int imx_ocotp_write(void *context, const void *data, size_t count)
{
/* Not implemented */
return 0;
}
static struct regmap_bus imx_ocotp_bus = {
.read = imx_ocotp_read,
.write = imx_ocotp_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static bool imx_ocotp_writeable_reg(struct device *dev, unsigned int reg)
{
return false;
}
static struct regmap_config imx_ocotp_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.writeable_reg = imx_ocotp_writeable_reg,
.name = "imx-ocotp",
};
static struct nvmem_config imx_ocotp_nvmem_config = {
.name = "imx-ocotp",
.read_only = true,
.word_size = 4,
.stride = 4,
.owner = THIS_MODULE,
.reg_read = imx_ocotp_read,
};
static const struct of_device_id imx_ocotp_dt_ids[] = {
@ -99,7 +73,6 @@ static int imx_ocotp_probe(struct platform_device *pdev)
const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct resource *res;
struct regmap *regmap;
struct ocotp_priv *priv;
struct nvmem_device *nvmem;
@ -114,15 +87,9 @@ static int imx_ocotp_probe(struct platform_device *pdev)
of_id = of_match_device(imx_ocotp_dt_ids, dev);
priv->nregs = (unsigned int)of_id->data;
imx_ocotp_regmap_config.max_register = 4 * priv->nregs - 4;
regmap = devm_regmap_init(dev, &imx_ocotp_bus, priv,
&imx_ocotp_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(regmap);
}
imx_ocotp_nvmem_config.size = 4 * priv->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
nvmem = nvmem_register(&imx_ocotp_nvmem_config);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);

View File

@ -16,7 +16,6 @@
#include <linux/module.h>
#include <linux/nvmem-provider.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
/* Registers */
@ -51,12 +50,7 @@ struct lpc18xx_eeprom_dev {
struct nvmem_device *nvmem;
unsigned reg_bytes;
unsigned val_bytes;
};
static struct regmap_config lpc18xx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
int size;
};
static inline void lpc18xx_eeprom_writel(struct lpc18xx_eeprom_dev *eeprom,
@ -95,30 +89,35 @@ static int lpc18xx_eeprom_busywait_until_prog(struct lpc18xx_eeprom_dev *eeprom)
return -ETIMEDOUT;
}
static int lpc18xx_eeprom_gather_write(void *context, const void *reg,
size_t reg_size, const void *val,
size_t val_size)
static int lpc18xx_eeprom_gather_write(void *context, unsigned int reg,
void *val, size_t bytes)
{
struct lpc18xx_eeprom_dev *eeprom = context;
unsigned int offset = *(u32 *)reg;
unsigned int offset = reg;
int ret;
if (offset % lpc18xx_regmap_config.reg_stride)
/*
* The last page contains the EEPROM initialization data and is not
* writable.
*/
if ((reg > eeprom->size - LPC18XX_EEPROM_PAGE_SIZE) ||
(reg + bytes > eeprom->size - LPC18XX_EEPROM_PAGE_SIZE))
return -EINVAL;
lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
LPC18XX_EEPROM_PWRDWN_NO);
/* Wait 100 us while the EEPROM wakes up */
usleep_range(100, 200);
while (val_size) {
while (bytes) {
writel(*(u32 *)val, eeprom->mem_base + offset);
ret = lpc18xx_eeprom_busywait_until_prog(eeprom);
if (ret < 0)
return ret;
val_size -= eeprom->val_bytes;
bytes -= eeprom->val_bytes;
val += eeprom->val_bytes;
offset += eeprom->val_bytes;
}
@ -129,23 +128,10 @@ static int lpc18xx_eeprom_gather_write(void *context, const void *reg,
return 0;
}
static int lpc18xx_eeprom_write(void *context, const void *data, size_t count)
static int lpc18xx_eeprom_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
struct lpc18xx_eeprom_dev *eeprom = context;
unsigned int offset = eeprom->reg_bytes;
if (count <= offset)
return -EINVAL;
return lpc18xx_eeprom_gather_write(context, data, eeprom->reg_bytes,
data + offset, count - offset);
}
static int lpc18xx_eeprom_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct lpc18xx_eeprom_dev *eeprom = context;
unsigned int offset = *(u32 *)reg;
lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
LPC18XX_EEPROM_PWRDWN_NO);
@ -153,9 +139,9 @@ static int lpc18xx_eeprom_read(void *context, const void *reg, size_t reg_size,
/* Wait 100 us while the EEPROM wakes up */
usleep_range(100, 200);
while (val_size) {
while (bytes) {
*(u32 *)val = readl(eeprom->mem_base + offset);
val_size -= eeprom->val_bytes;
bytes -= eeprom->val_bytes;
val += eeprom->val_bytes;
offset += eeprom->val_bytes;
}
@ -166,31 +152,13 @@ static int lpc18xx_eeprom_read(void *context, const void *reg, size_t reg_size,
return 0;
}
static struct regmap_bus lpc18xx_eeprom_bus = {
.write = lpc18xx_eeprom_write,
.gather_write = lpc18xx_eeprom_gather_write,
.read = lpc18xx_eeprom_read,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static bool lpc18xx_eeprom_writeable_reg(struct device *dev, unsigned int reg)
{
/*
* The last page contains the EEPROM initialization data and is not
* writable.
*/
return reg <= lpc18xx_regmap_config.max_register -
LPC18XX_EEPROM_PAGE_SIZE;
}
static bool lpc18xx_eeprom_readable_reg(struct device *dev, unsigned int reg)
{
return reg <= lpc18xx_regmap_config.max_register;
}
static struct nvmem_config lpc18xx_nvmem_config = {
.name = "lpc18xx-eeprom",
.stride = 4,
.word_size = 4,
.reg_read = lpc18xx_eeprom_read,
.reg_write = lpc18xx_eeprom_gather_write,
.owner = THIS_MODULE,
};
@ -200,7 +168,6 @@ static int lpc18xx_eeprom_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct reset_control *rst;
unsigned long clk_rate;
struct regmap *regmap;
struct resource *res;
int ret;
@ -243,8 +210,8 @@ static int lpc18xx_eeprom_probe(struct platform_device *pdev)
goto err_clk;
}
eeprom->val_bytes = lpc18xx_regmap_config.val_bits / BITS_PER_BYTE;
eeprom->reg_bytes = lpc18xx_regmap_config.reg_bits / BITS_PER_BYTE;
eeprom->val_bytes = 4;
eeprom->reg_bytes = 4;
/*
* Clock rate is generated by dividing the system bus clock by the
@ -264,19 +231,10 @@ static int lpc18xx_eeprom_probe(struct platform_device *pdev)
lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
LPC18XX_EEPROM_PWRDWN_YES);
lpc18xx_regmap_config.max_register = resource_size(res) - 1;
lpc18xx_regmap_config.writeable_reg = lpc18xx_eeprom_writeable_reg;
lpc18xx_regmap_config.readable_reg = lpc18xx_eeprom_readable_reg;
regmap = devm_regmap_init(dev, &lpc18xx_eeprom_bus, eeprom,
&lpc18xx_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "regmap init failed: %ld\n", PTR_ERR(regmap));
ret = PTR_ERR(regmap);
goto err_clk;
}
eeprom->size = resource_size(res);
lpc18xx_nvmem_config.size = resource_size(res);
lpc18xx_nvmem_config.dev = dev;
lpc18xx_nvmem_config.priv = eeprom;
eeprom->nvmem = nvmem_register(&lpc18xx_nvmem_config);
if (IS_ERR(eeprom->nvmem)) {

View File

@ -13,21 +13,35 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/nvmem-provider.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
static struct regmap_config qfprom_regmap_config = {
.reg_bits = 32,
.val_bits = 8,
.reg_stride = 1,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
static int qfprom_reg_read(void *context,
unsigned int reg, void *_val, size_t bytes)
{
void __iomem *base = context;
u32 *val = _val;
int i = 0, words = bytes / 4;
static struct nvmem_config econfig = {
.name = "qfprom",
.owner = THIS_MODULE,
};
while (words--)
*val++ = readl(base + reg + (i++ * 4));
return 0;
}
static int qfprom_reg_write(void *context,
unsigned int reg, void *_val, size_t bytes)
{
void __iomem *base = context;
u32 *val = _val;
int i = 0, words = bytes / 4;
while (words--)
writel(*val++, base + reg + (i++ * 4));
return 0;
}
static int qfprom_remove(struct platform_device *pdev)
{
@ -36,12 +50,20 @@ static int qfprom_remove(struct platform_device *pdev)
return nvmem_unregister(nvmem);
}
static struct nvmem_config econfig = {
.name = "qfprom",
.owner = THIS_MODULE,
.stride = 4,
.word_size = 1,
.reg_read = qfprom_reg_read,
.reg_write = qfprom_reg_write,
};
static int qfprom_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct nvmem_device *nvmem;
struct regmap *regmap;
void __iomem *base;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -49,14 +71,10 @@ static int qfprom_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
qfprom_regmap_config.max_register = resource_size(res) - 1;
regmap = devm_regmap_init_mmio(dev, base, &qfprom_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(regmap);
}
econfig.size = resource_size(res);
econfig.dev = dev;
econfig.priv = base;
nvmem = nvmem_register(&econfig);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);

View File

@ -23,7 +23,6 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define EFUSE_A_SHIFT 6
#define EFUSE_A_MASK 0x3ff
@ -41,17 +40,9 @@ struct rockchip_efuse_chip {
struct clk *clk;
};
static int rockchip_efuse_write(void *context, const void *data, size_t count)
static int rockchip_efuse_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
/* Nothing TBD, Read-Only */
return 0;
}
static int rockchip_efuse_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
unsigned int offset = *(u32 *)reg;
struct rockchip_efuse_chip *efuse = context;
u8 *buf = val;
int ret;
@ -64,12 +55,12 @@ static int rockchip_efuse_read(void *context,
writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + REG_EFUSE_CTRL);
udelay(1);
while (val_size) {
while (bytes--) {
writel(readl(efuse->base + REG_EFUSE_CTRL) &
(~(EFUSE_A_MASK << EFUSE_A_SHIFT)),
efuse->base + REG_EFUSE_CTRL);
writel(readl(efuse->base + REG_EFUSE_CTRL) |
((offset & EFUSE_A_MASK) << EFUSE_A_SHIFT),
((offset++ & EFUSE_A_MASK) << EFUSE_A_SHIFT),
efuse->base + REG_EFUSE_CTRL);
udelay(1);
writel(readl(efuse->base + REG_EFUSE_CTRL) |
@ -79,9 +70,6 @@ static int rockchip_efuse_read(void *context,
writel(readl(efuse->base + REG_EFUSE_CTRL) &
(~EFUSE_STROBE), efuse->base + REG_EFUSE_CTRL);
udelay(1);
val_size -= 1;
offset += 1;
}
/* Switch to standby mode */
@ -92,22 +80,11 @@ static int rockchip_efuse_read(void *context,
return 0;
}
static struct regmap_bus rockchip_efuse_bus = {
.read = rockchip_efuse_read,
.write = rockchip_efuse_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static struct regmap_config rockchip_efuse_regmap_config = {
.reg_bits = 32,
.reg_stride = 1,
.val_bits = 8,
};
static struct nvmem_config econfig = {
.name = "rockchip-efuse",
.owner = THIS_MODULE,
.stride = 1,
.word_size = 1,
.read_only = true,
};
@ -121,7 +98,6 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
{
struct resource *res;
struct nvmem_device *nvmem;
struct regmap *regmap;
struct rockchip_efuse_chip *efuse;
efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip),
@ -139,16 +115,9 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
return PTR_ERR(efuse->clk);
efuse->dev = &pdev->dev;
rockchip_efuse_regmap_config.max_register = resource_size(res) - 1;
regmap = devm_regmap_init(efuse->dev, &rockchip_efuse_bus,
efuse, &rockchip_efuse_regmap_config);
if (IS_ERR(regmap)) {
dev_err(efuse->dev, "regmap init failed\n");
return PTR_ERR(regmap);
}
econfig.size = resource_size(res);
econfig.reg_read = rockchip_efuse_read;
econfig.priv = efuse;
econfig.dev = efuse->dev;
nvmem = nvmem_register(&econfig);
if (IS_ERR(nvmem))

View File

@ -21,13 +21,14 @@
#include <linux/nvmem-provider.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/random.h>
static struct nvmem_config econfig = {
.name = "sunxi-sid",
.read_only = true,
.stride = 4,
.word_size = 1,
.owner = THIS_MODULE,
};
@ -51,54 +52,23 @@ static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid,
return sid_key; /* Only return the last byte */
}
static int sunxi_sid_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
static int sunxi_sid_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
struct sunxi_sid *sid = context;
unsigned int offset = *(u32 *)reg;
u8 *buf = val;
while (val_size) {
*buf++ = sunxi_sid_read_byte(sid, offset);
val_size--;
offset++;
}
while (bytes--)
*buf++ = sunxi_sid_read_byte(sid, offset++);
return 0;
}
static int sunxi_sid_write(void *context, const void *data, size_t count)
{
/* Unimplemented, dummy to keep regmap core happy */
return 0;
}
static struct regmap_bus sunxi_sid_bus = {
.read = sunxi_sid_read,
.write = sunxi_sid_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static bool sunxi_sid_writeable_reg(struct device *dev, unsigned int reg)
{
return false;
}
static struct regmap_config sunxi_sid_regmap_config = {
.reg_bits = 32,
.val_bits = 8,
.reg_stride = 1,
.writeable_reg = sunxi_sid_writeable_reg,
};
static int sunxi_sid_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct nvmem_device *nvmem;
struct regmap *regmap;
struct sunxi_sid *sid;
int ret, i, size;
char *randomness;
@ -113,16 +83,10 @@ static int sunxi_sid_probe(struct platform_device *pdev)
return PTR_ERR(sid->base);
size = resource_size(res) - 1;
sunxi_sid_regmap_config.max_register = size;
regmap = devm_regmap_init(dev, &sunxi_sid_bus, sid,
&sunxi_sid_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(regmap);
}
econfig.size = resource_size(res);
econfig.dev = dev;
econfig.reg_read = sunxi_sid_read;
econfig.priv = sid;
nvmem = nvmem_register(&econfig);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);

View File

@ -25,7 +25,6 @@
#include <linux/nvmem-provider.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* OCOTP Register Offsets */
@ -152,23 +151,16 @@ static int vf610_get_fuse_address(int base_addr_offset)
return -EINVAL;
}
static int vf610_ocotp_write(void *context, const void *data, size_t count)
{
return 0;
}
static int vf610_ocotp_read(void *context,
const void *off, size_t reg_size,
void *val, size_t val_size)
static int vf610_ocotp_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
struct vf610_ocotp *ocotp = context;
void __iomem *base = ocotp->base;
unsigned int offset = *(u32 *)off;
u32 reg, *buf = val;
int fuse_addr;
int ret;
while (val_size > 0) {
while (bytes > 0) {
fuse_addr = vf610_get_fuse_address(offset);
if (fuse_addr > 0) {
writel(ocotp->timing, base + OCOTP_TIMING);
@ -205,29 +197,19 @@ static int vf610_ocotp_read(void *context,
}
buf++;
val_size--;
offset += reg_size;
bytes -= 4;
offset += 4;
}
return 0;
}
static struct regmap_bus vf610_ocotp_bus = {
.read = vf610_ocotp_read,
.write = vf610_ocotp_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static struct regmap_config ocotp_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static struct nvmem_config ocotp_config = {
.name = "ocotp",
.owner = THIS_MODULE,
.stride = 4,
.word_size = 4,
.reg_read = vf610_ocotp_read,
};
static const struct of_device_id ocotp_of_match[] = {
@ -247,7 +229,6 @@ static int vf610_ocotp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct regmap *regmap;
struct vf610_ocotp *ocotp_dev;
ocotp_dev = devm_kzalloc(&pdev->dev,
@ -267,13 +248,8 @@ static int vf610_ocotp_probe(struct platform_device *pdev)
return PTR_ERR(ocotp_dev->clk);
}
ocotp_regmap_config.max_register = resource_size(res);
regmap = devm_regmap_init(dev,
&vf610_ocotp_bus, ocotp_dev, &ocotp_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(regmap);
}
ocotp_config.size = resource_size(res);
ocotp_config.priv = ocotp_dev;
ocotp_config.dev = dev;
ocotp_dev->nvmem = nvmem_register(&ocotp_config);

View File

@ -617,5 +617,5 @@ static void __exit parport_default_proc_unregister (void)
}
#endif
module_init(parport_default_proc_register)
subsys_initcall(parport_default_proc_register)
module_exit(parport_default_proc_unregister)

View File

@ -1809,14 +1809,14 @@ static void hv_pci_free_bridge_windows(struct hv_pcibus_device *hbus)
if (hbus->low_mmio_space && hbus->low_mmio_res) {
hbus->low_mmio_res->flags |= IORESOURCE_BUSY;
release_mem_region(hbus->low_mmio_res->start,
resource_size(hbus->low_mmio_res));
vmbus_free_mmio(hbus->low_mmio_res->start,
resource_size(hbus->low_mmio_res));
}
if (hbus->high_mmio_space && hbus->high_mmio_res) {
hbus->high_mmio_res->flags |= IORESOURCE_BUSY;
release_mem_region(hbus->high_mmio_res->start,
resource_size(hbus->high_mmio_res));
vmbus_free_mmio(hbus->high_mmio_res->start,
resource_size(hbus->high_mmio_res));
}
}
@ -1894,8 +1894,8 @@ static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus)
release_low_mmio:
if (hbus->low_mmio_res) {
release_mem_region(hbus->low_mmio_res->start,
resource_size(hbus->low_mmio_res));
vmbus_free_mmio(hbus->low_mmio_res->start,
resource_size(hbus->low_mmio_res));
}
return ret;
@ -1938,7 +1938,7 @@ static int hv_allocate_config_window(struct hv_pcibus_device *hbus)
static void hv_free_config_window(struct hv_pcibus_device *hbus)
{
release_mem_region(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH);
vmbus_free_mmio(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH);
}
/**

View File

@ -25,6 +25,7 @@
#define CREATE_TRACE_POINTS
#include <trace/events/spmi.h>
static bool is_registered;
static DEFINE_IDA(ctrl_ida);
static void spmi_dev_release(struct device *dev)
@ -507,7 +508,7 @@ int spmi_controller_add(struct spmi_controller *ctrl)
int ret;
/* Can't register until after driver model init */
if (WARN_ON(!spmi_bus_type.p))
if (WARN_ON(!is_registered))
return -EAGAIN;
ret = device_add(&ctrl->dev);
@ -576,7 +577,14 @@ module_exit(spmi_exit);
static int __init spmi_init(void)
{
return bus_register(&spmi_bus_type);
int ret;
ret = bus_register(&spmi_bus_type);
if (ret)
return ret;
is_registered = true;
return 0;
}
postcore_initcall(spmi_init);

View File

@ -271,12 +271,16 @@ static int uio_dev_add_attributes(struct uio_device *idev)
map_found = 1;
idev->map_dir = kobject_create_and_add("maps",
&idev->dev->kobj);
if (!idev->map_dir)
if (!idev->map_dir) {
ret = -ENOMEM;
goto err_map;
}
}
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
if (!map) {
ret = -ENOMEM;
goto err_map_kobj;
}
kobject_init(&map->kobj, &map_attr_type);
map->mem = mem;
mem->map = map;
@ -296,12 +300,16 @@ static int uio_dev_add_attributes(struct uio_device *idev)
portio_found = 1;
idev->portio_dir = kobject_create_and_add("portio",
&idev->dev->kobj);
if (!idev->portio_dir)
if (!idev->portio_dir) {
ret = -ENOMEM;
goto err_portio;
}
}
portio = kzalloc(sizeof(*portio), GFP_KERNEL);
if (!portio)
if (!portio) {
ret = -ENOMEM;
goto err_portio_kobj;
}
kobject_init(&portio->kobj, &portio_attr_type);
portio->port = port;
port->portio = portio;

View File

@ -743,7 +743,7 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
err3:
iounmap(fb_virt);
err2:
release_mem_region(par->mem->start, screen_fb_size);
vmbus_free_mmio(par->mem->start, screen_fb_size);
par->mem = NULL;
err1:
if (!gen2vm)
@ -758,7 +758,7 @@ static void hvfb_putmem(struct fb_info *info)
struct hvfb_par *par = info->par;
iounmap(info->screen_base);
release_mem_region(par->mem->start, screen_fb_size);
vmbus_free_mmio(par->mem->start, screen_fb_size);
par->mem = NULL;
}

View File

@ -204,10 +204,6 @@ static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge)
/* Need pdev */
pdev = to_pci_dev(ca91cx42_bridge->parent);
INIT_LIST_HEAD(&ca91cx42_bridge->vme_error_handlers);
mutex_init(&ca91cx42_bridge->irq_mtx);
/* Disable interrupts from PCI to VME */
iowrite32(0, bridge->base + VINT_EN);
@ -1626,6 +1622,7 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
retval = -ENOMEM;
goto err_struct;
}
vme_init_bridge(ca91cx42_bridge);
ca91cx42_device = kzalloc(sizeof(struct ca91cx42_driver), GFP_KERNEL);
@ -1686,7 +1683,6 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add master windows to list */
INIT_LIST_HEAD(&ca91cx42_bridge->master_resources);
for (i = 0; i < CA91C142_MAX_MASTER; i++) {
master_image = kmalloc(sizeof(struct vme_master_resource),
GFP_KERNEL);
@ -1713,7 +1709,6 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add slave windows to list */
INIT_LIST_HEAD(&ca91cx42_bridge->slave_resources);
for (i = 0; i < CA91C142_MAX_SLAVE; i++) {
slave_image = kmalloc(sizeof(struct vme_slave_resource),
GFP_KERNEL);
@ -1741,7 +1736,6 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add dma engines to list */
INIT_LIST_HEAD(&ca91cx42_bridge->dma_resources);
for (i = 0; i < CA91C142_MAX_DMA; i++) {
dma_ctrlr = kmalloc(sizeof(struct vme_dma_resource),
GFP_KERNEL);
@ -1764,7 +1758,6 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add location monitor to list */
INIT_LIST_HEAD(&ca91cx42_bridge->lm_resources);
lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL);
if (lm == NULL) {
dev_err(&pdev->dev, "Failed to allocate memory for "

View File

@ -314,10 +314,6 @@ static int tsi148_irq_init(struct vme_bridge *tsi148_bridge)
bridge = tsi148_bridge->driver_priv;
INIT_LIST_HEAD(&tsi148_bridge->vme_error_handlers);
mutex_init(&tsi148_bridge->irq_mtx);
result = request_irq(pdev->irq,
tsi148_irqhandler,
IRQF_SHARED,
@ -2301,6 +2297,7 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
retval = -ENOMEM;
goto err_struct;
}
vme_init_bridge(tsi148_bridge);
tsi148_device = kzalloc(sizeof(struct tsi148_driver), GFP_KERNEL);
if (tsi148_device == NULL) {
@ -2387,7 +2384,6 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add master windows to list */
INIT_LIST_HEAD(&tsi148_bridge->master_resources);
for (i = 0; i < master_num; i++) {
master_image = kmalloc(sizeof(struct vme_master_resource),
GFP_KERNEL);
@ -2417,7 +2413,6 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add slave windows to list */
INIT_LIST_HEAD(&tsi148_bridge->slave_resources);
for (i = 0; i < TSI148_MAX_SLAVE; i++) {
slave_image = kmalloc(sizeof(struct vme_slave_resource),
GFP_KERNEL);
@ -2442,7 +2437,6 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add dma engines to list */
INIT_LIST_HEAD(&tsi148_bridge->dma_resources);
for (i = 0; i < TSI148_MAX_DMA; i++) {
dma_ctrlr = kmalloc(sizeof(struct vme_dma_resource),
GFP_KERNEL);
@ -2467,7 +2461,6 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* Add location monitor to list */
INIT_LIST_HEAD(&tsi148_bridge->lm_resources);
lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL);
if (lm == NULL) {
dev_err(&pdev->dev, "Failed to allocate memory for "

View File

@ -782,7 +782,7 @@ struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
dma_list = kmalloc(sizeof(struct vme_dma_list), GFP_KERNEL);
if (dma_list == NULL) {
printk(KERN_ERR "Unable to allocate memory for new dma list\n");
printk(KERN_ERR "Unable to allocate memory for new DMA list\n");
return NULL;
}
INIT_LIST_HEAD(&dma_list->entries);
@ -846,7 +846,7 @@ struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address)
pci_attr = kmalloc(sizeof(struct vme_dma_pci), GFP_KERNEL);
if (pci_attr == NULL) {
printk(KERN_ERR "Unable to allocate memory for pci attributes\n");
printk(KERN_ERR "Unable to allocate memory for PCI attributes\n");
goto err_pci;
}
@ -884,7 +884,7 @@ struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address,
vme_attr = kmalloc(sizeof(struct vme_dma_vme), GFP_KERNEL);
if (vme_attr == NULL) {
printk(KERN_ERR "Unable to allocate memory for vme attributes\n");
printk(KERN_ERR "Unable to allocate memory for VME attributes\n");
goto err_vme;
}
@ -975,8 +975,8 @@ int vme_dma_list_free(struct vme_dma_list *list)
}
/*
* Empty out all of the entries from the dma list. We need to go to the
* low level driver as dma entries are driver specific.
* Empty out all of the entries from the DMA list. We need to go to the
* low level driver as DMA entries are driver specific.
*/
retval = bridge->dma_list_empty(list);
if (retval) {
@ -1091,7 +1091,7 @@ void vme_irq_handler(struct vme_bridge *bridge, int level, int statid)
if (call != NULL)
call(level, statid, priv_data);
else
printk(KERN_WARNING "Spurilous VME interrupt, level:%x, vector:%x\n",
printk(KERN_WARNING "Spurious VME interrupt, level:%x, vector:%x\n",
level, statid);
}
EXPORT_SYMBOL(vme_irq_handler);
@ -1429,6 +1429,20 @@ static void vme_dev_release(struct device *dev)
kfree(dev_to_vme_dev(dev));
}
/* Common bridge initialization */
struct vme_bridge *vme_init_bridge(struct vme_bridge *bridge)
{
INIT_LIST_HEAD(&bridge->vme_error_handlers);
INIT_LIST_HEAD(&bridge->master_resources);
INIT_LIST_HEAD(&bridge->slave_resources);
INIT_LIST_HEAD(&bridge->dma_resources);
INIT_LIST_HEAD(&bridge->lm_resources);
mutex_init(&bridge->irq_mtx);
return bridge;
}
EXPORT_SYMBOL(vme_init_bridge);
int vme_register_bridge(struct vme_bridge *bridge)
{
int i;

View File

@ -177,6 +177,7 @@ void vme_bus_error_handler(struct vme_bridge *bridge,
unsigned long long address, int am);
void vme_irq_handler(struct vme_bridge *, int, int);
struct vme_bridge *vme_init_bridge(struct vme_bridge *);
int vme_register_bridge(struct vme_bridge *);
void vme_unregister_bridge(struct vme_bridge *);
struct vme_error_handler *vme_register_error_handler(

View File

@ -23,6 +23,19 @@
#include "../w1.h"
#include "../w1_int.h"
/**
* Allow the active pullup to be disabled, default is enabled.
*
* Note from the DS2482 datasheet:
* The APU bit controls whether an active pullup (controlled slew-rate
* transistor) or a passive pullup (Rwpu resistor) will be used to drive
* a 1-Wire line from low to high. When APU = 0, active pullup is disabled
* (resistor mode). Active Pullup should always be selected unless there is
* only a single slave on the 1-Wire line.
*/
static int ds2482_active_pullup = 1;
module_param_named(active_pullup, ds2482_active_pullup, int, 0644);
/**
* The DS2482 registers - there are 3 registers that are addressed by a read
* pointer. The read pointer is set by the last command executed.
@ -138,6 +151,9 @@ struct ds2482_data {
*/
static inline u8 ds2482_calculate_config(u8 conf)
{
if (ds2482_active_pullup)
conf |= DS2482_REG_CFG_APU;
return conf | ((~conf & 0x0f) << 4);
}
@ -546,6 +562,8 @@ static int ds2482_remove(struct i2c_client *client)
module_i2c_driver(ds2482_driver);
MODULE_PARM_DESC(active_pullup, "Active pullup (apply to all buses): " \
"0-disable, 1-enable (default)");
MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
MODULE_DESCRIPTION("DS2482 driver");
MODULE_LICENSE("GPL");

View File

@ -92,10 +92,13 @@ static void w1_therm_remove_slave(struct w1_slave *sl)
static ssize_t w1_slave_show(struct device *device,
struct device_attribute *attr, char *buf);
static ssize_t w1_slave_store(struct device *device,
struct device_attribute *attr, const char *buf, size_t size);
static ssize_t w1_seq_show(struct device *device,
struct device_attribute *attr, char *buf);
static DEVICE_ATTR_RO(w1_slave);
static DEVICE_ATTR_RW(w1_slave);
static DEVICE_ATTR_RO(w1_seq);
static struct attribute *w1_therm_attrs[] = {
@ -154,8 +157,17 @@ struct w1_therm_family_converter
u16 reserved;
struct w1_family *f;
int (*convert)(u8 rom[9]);
int (*precision)(struct device *device, int val);
int (*eeprom)(struct device *device);
};
/* write configuration to eeprom */
static inline int w1_therm_eeprom(struct device *device);
/* Set precision for conversion */
static inline int w1_DS18B20_precision(struct device *device, int val);
static inline int w1_DS18S20_precision(struct device *device, int val);
/* The return value is millidegrees Centigrade. */
static inline int w1_DS18B20_convert_temp(u8 rom[9]);
static inline int w1_DS18S20_convert_temp(u8 rom[9]);
@ -163,26 +175,194 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9]);
static struct w1_therm_family_converter w1_therm_families[] = {
{
.f = &w1_therm_family_DS18S20,
.convert = w1_DS18S20_convert_temp
.convert = w1_DS18S20_convert_temp,
.precision = w1_DS18S20_precision,
.eeprom = w1_therm_eeprom
},
{
.f = &w1_therm_family_DS1822,
.convert = w1_DS18B20_convert_temp
.convert = w1_DS18B20_convert_temp,
.precision = w1_DS18S20_precision,
.eeprom = w1_therm_eeprom
},
{
.f = &w1_therm_family_DS18B20,
.convert = w1_DS18B20_convert_temp
.convert = w1_DS18B20_convert_temp,
.precision = w1_DS18B20_precision,
.eeprom = w1_therm_eeprom
},
{
.f = &w1_therm_family_DS28EA00,
.convert = w1_DS18B20_convert_temp
.convert = w1_DS18B20_convert_temp,
.precision = w1_DS18S20_precision,
.eeprom = w1_therm_eeprom
},
{
.f = &w1_therm_family_DS1825,
.convert = w1_DS18B20_convert_temp
.convert = w1_DS18B20_convert_temp,
.precision = w1_DS18S20_precision,
.eeprom = w1_therm_eeprom
}
};
static inline int w1_therm_eeprom(struct device *device)
{
struct w1_slave *sl = dev_to_w1_slave(device);
struct w1_master *dev = sl->master;
u8 rom[9], external_power;
int ret, max_trying = 10;
u8 *family_data = sl->family_data;
ret = mutex_lock_interruptible(&dev->bus_mutex);
if (ret != 0)
goto post_unlock;
if (!sl->family_data) {
ret = -ENODEV;
goto pre_unlock;
}
/* prevent the slave from going away in sleep */
atomic_inc(THERM_REFCNT(family_data));
memset(rom, 0, sizeof(rom));
while (max_trying--) {
if (!w1_reset_select_slave(sl)) {
unsigned int tm = 10;
unsigned long sleep_rem;
/* check if in parasite mode */
w1_write_8(dev, W1_READ_PSUPPLY);
external_power = w1_read_8(dev);
if (w1_reset_select_slave(sl))
continue;
/* 10ms strong pullup/delay after the copy command */
if (w1_strong_pullup == 2 ||
(!external_power && w1_strong_pullup))
w1_next_pullup(dev, tm);
w1_write_8(dev, W1_COPY_SCRATCHPAD);
if (external_power) {
mutex_unlock(&dev->bus_mutex);
sleep_rem = msleep_interruptible(tm);
if (sleep_rem != 0) {
ret = -EINTR;
goto post_unlock;
}
ret = mutex_lock_interruptible(&dev->bus_mutex);
if (ret != 0)
goto post_unlock;
} else if (!w1_strong_pullup) {
sleep_rem = msleep_interruptible(tm);
if (sleep_rem != 0) {
ret = -EINTR;
goto pre_unlock;
}
}
break;
}
}
pre_unlock:
mutex_unlock(&dev->bus_mutex);
post_unlock:
atomic_dec(THERM_REFCNT(family_data));
return ret;
}
/* DS18S20 does not feature configuration register */
static inline int w1_DS18S20_precision(struct device *device, int val)
{
return 0;
}
static inline int w1_DS18B20_precision(struct device *device, int val)
{
struct w1_slave *sl = dev_to_w1_slave(device);
struct w1_master *dev = sl->master;
u8 rom[9], crc;
int ret, max_trying = 10;
u8 *family_data = sl->family_data;
uint8_t precision_bits;
uint8_t mask = 0x60;
if(val > 12 || val < 9) {
pr_warn("Unsupported precision\n");
return -1;
}
ret = mutex_lock_interruptible(&dev->bus_mutex);
if (ret != 0)
goto post_unlock;
if (!sl->family_data) {
ret = -ENODEV;
goto pre_unlock;
}
/* prevent the slave from going away in sleep */
atomic_inc(THERM_REFCNT(family_data));
memset(rom, 0, sizeof(rom));
/* translate precision to bitmask (see datasheet page 9) */
switch (val) {
case 9:
precision_bits = 0x00;
break;
case 10:
precision_bits = 0x20;
break;
case 11:
precision_bits = 0x40;
break;
case 12:
default:
precision_bits = 0x60;
break;
}
while (max_trying--) {
crc = 0;
if (!w1_reset_select_slave(sl)) {
int count = 0;
/* read values to only alter precision bits */
w1_write_8(dev, W1_READ_SCRATCHPAD);
if ((count = w1_read_block(dev, rom, 9)) != 9)
dev_warn(device, "w1_read_block() returned %u instead of 9.\n", count);
crc = w1_calc_crc8(rom, 8);
if (rom[8] == crc) {
rom[4] = (rom[4] & ~mask) | (precision_bits & mask);
if (!w1_reset_select_slave(sl)) {
w1_write_8(dev, W1_WRITE_SCRATCHPAD);
w1_write_8(dev, rom[2]);
w1_write_8(dev, rom[3]);
w1_write_8(dev, rom[4]);
break;
}
}
}
}
pre_unlock:
mutex_unlock(&dev->bus_mutex);
post_unlock:
atomic_dec(THERM_REFCNT(family_data));
return ret;
}
static inline int w1_DS18B20_convert_temp(u8 rom[9])
{
s16 t = le16_to_cpup((__le16 *)rom);
@ -220,6 +400,30 @@ static inline int w1_convert_temp(u8 rom[9], u8 fid)
return 0;
}
static ssize_t w1_slave_store(struct device *device,
struct device_attribute *attr, const char *buf,
size_t size)
{
int val, ret;
struct w1_slave *sl = dev_to_w1_slave(device);
int i;
ret = kstrtoint(buf, 0, &val);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
if (w1_therm_families[i].f->fid == sl->family->fid) {
/* zero value indicates to write current configuration to eeprom */
if (0 == val)
ret = w1_therm_families[i].eeprom(device);
else
ret = w1_therm_families[i].precision(device, val);
break;
}
}
return ret ? : size;
}
static ssize_t w1_slave_show(struct device *device,
struct device_attribute *attr, char *buf)
@ -311,7 +515,7 @@ static ssize_t w1_slave_show(struct device *device,
for (i = 0; i < 9; ++i)
c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]);
c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
crc, (verdict) ? "YES" : "NO");
crc, (verdict) ? "YES" : "NO");
if (verdict)
memcpy(family_data, rom, sizeof(rom));
else

View File

@ -335,7 +335,7 @@ static ssize_t w1_master_attribute_store_max_slave_count(struct device *dev,
int tmp;
struct w1_master *md = dev_to_w1_master(dev);
if (kstrtoint(buf, 0, &tmp) == -EINVAL || tmp < 1)
if (kstrtoint(buf, 0, &tmp) || tmp < 1)
return -EINVAL;
mutex_lock(&md->mutex);

View File

@ -58,6 +58,8 @@ struct w1_reg_num
#define W1_ALARM_SEARCH 0xEC
#define W1_CONVERT_TEMP 0x44
#define W1_SKIP_ROM 0xCC
#define W1_COPY_SCRATCHPAD 0x48
#define W1_WRITE_SCRATCHPAD 0x4E
#define W1_READ_SCRATCHPAD 0xBE
#define W1_READ_ROM 0x33
#define W1_READ_PSUPPLY 0xB4

View File

@ -0,0 +1,6 @@
#ifndef __LINUX_CORESIGHT_STM_H_
#define __LINUX_CORESIGHT_STM_H_
#include <uapi/linux/coresight-stm.h>
#endif

View File

@ -126,6 +126,8 @@ struct hv_ring_buffer_info {
u32 ring_datasize; /* < ring_size */
u32 ring_data_startoffset;
u32 priv_write_index;
u32 priv_read_index;
};
/*
@ -151,6 +153,33 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
*read = dsize - *write;
}
static inline u32 hv_get_bytes_to_read(struct hv_ring_buffer_info *rbi)
{
u32 read_loc, write_loc, dsize, read;
dsize = rbi->ring_datasize;
read_loc = rbi->ring_buffer->read_index;
write_loc = READ_ONCE(rbi->ring_buffer->write_index);
read = write_loc >= read_loc ? (write_loc - read_loc) :
(dsize - read_loc) + write_loc;
return read;
}
static inline u32 hv_get_bytes_to_write(struct hv_ring_buffer_info *rbi)
{
u32 read_loc, write_loc, dsize, write;
dsize = rbi->ring_datasize;
read_loc = READ_ONCE(rbi->ring_buffer->read_index);
write_loc = rbi->ring_buffer->write_index;
write = write_loc >= read_loc ? dsize - (write_loc - read_loc) :
read_loc - write_loc;
return write;
}
/*
* VMBUS version is 32 bit entity broken up into
* two 16 bit quantities: major_number. minor_number.
@ -1091,7 +1120,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
resource_size_t min, resource_size_t max,
resource_size_t size, resource_size_t align,
bool fb_overlap_ok);
void vmbus_free_mmio(resource_size_t start, resource_size_t size);
int vmbus_cpu_number_to_vp_number(int cpu_number);
u64 hv_do_hypercall(u64 control, void *input, void *output);
@ -1338,4 +1367,143 @@ extern __u32 vmbus_proto_version;
int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id,
const uuid_le *shv_host_servie_id);
void vmbus_set_event(struct vmbus_channel *channel);
/* Get the start of the ring buffer. */
static inline void *
hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info)
{
return (void *)ring_info->ring_buffer->buffer;
}
/*
* To optimize the flow management on the send-side,
* when the sender is blocked because of lack of
* sufficient space in the ring buffer, potential the
* consumer of the ring buffer can signal the producer.
* This is controlled by the following parameters:
*
* 1. pending_send_sz: This is the size in bytes that the
* producer is trying to send.
* 2. The feature bit feat_pending_send_sz set to indicate if
* the consumer of the ring will signal when the ring
* state transitions from being full to a state where
* there is room for the producer to send the pending packet.
*/
static inline bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)
{
u32 cur_write_sz;
u32 pending_sz;
/*
* Issue a full memory barrier before making the signaling decision.
* Here is the reason for having this barrier:
* If the reading of the pend_sz (in this function)
* were to be reordered and read before we commit the new read
* index (in the calling function) we could
* have a problem. If the host were to set the pending_sz after we
* have sampled pending_sz and go to sleep before we commit the
* read index, we could miss sending the interrupt. Issue a full
* memory barrier to address this.
*/
virt_mb();
pending_sz = READ_ONCE(rbi->ring_buffer->pending_send_sz);
/* If the other end is not blocked on write don't bother. */
if (pending_sz == 0)
return false;
cur_write_sz = hv_get_bytes_to_write(rbi);
if (cur_write_sz >= pending_sz)
return true;
return false;
}
/*
* An API to support in-place processing of incoming VMBUS packets.
*/
#define VMBUS_PKT_TRAILER 8
static inline struct vmpacket_descriptor *
get_next_pkt_raw(struct vmbus_channel *channel)
{
struct hv_ring_buffer_info *ring_info = &channel->inbound;
u32 read_loc = ring_info->priv_read_index;
void *ring_buffer = hv_get_ring_buffer(ring_info);
struct vmpacket_descriptor *cur_desc;
u32 packetlen;
u32 dsize = ring_info->ring_datasize;
u32 delta = read_loc - ring_info->ring_buffer->read_index;
u32 bytes_avail_toread = (hv_get_bytes_to_read(ring_info) - delta);
if (bytes_avail_toread < sizeof(struct vmpacket_descriptor))
return NULL;
if ((read_loc + sizeof(*cur_desc)) > dsize)
return NULL;
cur_desc = ring_buffer + read_loc;
packetlen = cur_desc->len8 << 3;
/*
* If the packet under consideration is wrapping around,
* return failure.
*/
if ((read_loc + packetlen + VMBUS_PKT_TRAILER) > (dsize - 1))
return NULL;
return cur_desc;
}
/*
* A helper function to step through packets "in-place"
* This API is to be called after each successful call
* get_next_pkt_raw().
*/
static inline void put_pkt_raw(struct vmbus_channel *channel,
struct vmpacket_descriptor *desc)
{
struct hv_ring_buffer_info *ring_info = &channel->inbound;
u32 read_loc = ring_info->priv_read_index;
u32 packetlen = desc->len8 << 3;
u32 dsize = ring_info->ring_datasize;
if ((read_loc + packetlen + VMBUS_PKT_TRAILER) > dsize)
BUG();
/*
* Include the packet trailer.
*/
ring_info->priv_read_index += packetlen + VMBUS_PKT_TRAILER;
}
/*
* This call commits the read index and potentially signals the host.
* Here is the pattern for using the "in-place" consumption APIs:
*
* while (get_next_pkt_raw() {
* process the packet "in-place";
* put_pkt_raw();
* }
* if (packets processed in place)
* commit_rd_index();
*/
static inline void commit_rd_index(struct vmbus_channel *channel)
{
struct hv_ring_buffer_info *ring_info = &channel->inbound;
/*
* Make sure all reads are done before we update the read index since
* the writer may start writing to the read area once the read index
* is updated.
*/
virt_rmb();
ring_info->ring_buffer->read_index = ring_info->priv_read_index;
if (hv_need_to_signal_on_read(ring_info))
vmbus_set_event(channel);
}
#endif /* _HYPERV_H */

View File

@ -15,22 +15,30 @@
#include <linux/device.h>
#include <linux/irqreturn.h>
#define CHAMELEON_FILENAME_LEN 12
struct mcb_driver;
struct mcb_device;
/**
* struct mcb_bus - MEN Chameleon Bus
*
* @dev: pointer to carrier device
* @children: the child busses
* @dev: bus device
* @carrier: pointer to carrier device
* @bus_nr: mcb bus number
* @get_irq: callback to get IRQ number
* @revision: the FPGA's revision number
* @model: the FPGA's model number
* @filename: the FPGA's name
*/
struct mcb_bus {
struct list_head children;
struct device dev;
struct device *carrier;
int bus_nr;
u8 revision;
char model;
u8 minor;
char name[CHAMELEON_FILENAME_LEN + 1];
int (*get_irq)(struct mcb_device *dev);
};
#define to_mcb_bus(b) container_of((b), struct mcb_bus, dev)

View File

@ -14,6 +14,10 @@
struct nvmem_device;
struct nvmem_cell_info;
typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
void *val, size_t bytes);
typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
void *val, size_t bytes);
struct nvmem_config {
struct device *dev;
@ -24,6 +28,12 @@ struct nvmem_config {
int ncells;
bool read_only;
bool root_only;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
int size;
int word_size;
int stride;
void *priv;
/* To be only used by old driver/misc/eeprom drivers */
bool compat;
struct device *base_dev;

View File

@ -50,6 +50,8 @@ struct stm_device;
* @sw_end: last STP master available to software
* @sw_nchannels: number of STP channels per master
* @sw_mmiosz: size of one channel's IO space, for mmap, optional
* @hw_override: masters in the STP stream will not match the ones
* assigned by software, but are up to the STM hardware
* @packet: callback that sends an STP packet
* @mmio_addr: mmap callback, optional
* @link: called when a new stm_source gets linked to us, optional
@ -85,6 +87,7 @@ struct stm_data {
unsigned int sw_end;
unsigned int sw_nchannels;
unsigned int sw_mmiosz;
unsigned int hw_override;
ssize_t (*packet)(struct stm_data *, unsigned int,
unsigned int, unsigned int,
unsigned int, unsigned int,

View File

@ -0,0 +1,21 @@
#ifndef __UAPI_CORESIGHT_STM_H_
#define __UAPI_CORESIGHT_STM_H_
#define STM_FLAG_TIMESTAMPED BIT(3)
#define STM_FLAG_GUARANTEED BIT(7)
/*
* The CoreSight STM supports guaranteed and invariant timing
* transactions. Guaranteed transactions are guaranteed to be
* traced, this might involve stalling the bus or system to
* ensure the transaction is accepted by the STM. While invariant
* timing transactions are not guaranteed to be traced, they
* will take an invariant amount of time regardless of the
* state of the STM.
*/
enum {
STM_OPTION_GUARANTEED = 0,
STM_OPTION_INVARIANT,
};
#endif

View File

@ -89,7 +89,7 @@ def parse_options():
if opts.diff and not re.match(r"^[\w\-\.]+\.\.[\w\-\.]+$", opts.diff):
sys.exit("Please specify valid input in the following format: "
"\'commmit1..commit2\'")
"\'commit1..commit2\'")
if opts.commit or opts.diff:
if not opts.force and tree_is_dirty():

View File

@ -35,6 +35,7 @@ vmbus_dev_dict = {
'{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}' : 'Synthetic SCSI Controller',
'{2f9bcc4a-0069-4af3-b76b-6fd0be528cda}' : 'Synthetic fiber channel adapter',
'{8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}' : 'Synthetic RDMA adapter',
'{44c4f61d-4444-4400-9d52-802e27ede19f}' : 'PCI Express pass-through',
'{276aacf4-ac15-426c-98dd-7521ad3f01fe}' : '[Reserved system device]',
'{f8e65716-3cb3-4a06-9a60-1889c5cccab5}' : '[Reserved system device]',
'{3375baf4-9e15-4b30-b765-67acb10d607b}' : '[Reserved system device]',