From a6341f000024cdf1ec14dc26743a409a17378db5 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 2 Apr 2016 17:59:46 -0700 Subject: Drivers: hv: vmbus: Introduce functions for estimating room in the ring buffer Introduce separate functions for estimating how much can be read from and written to the ring buffer. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'include/linux') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index aa0fadce9308..66226ceade37 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -151,6 +151,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. -- cgit v1.2.3-71-gd317 From 5cc472477f928fb8584eb8e08245c9cf9002d74a Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 2 Apr 2016 17:59:49 -0700 Subject: Drivers: hv: vmbus: Export the vmbus_set_event() API In preparation for moving some ring buffer functionality out of the vmbus driver, export the API for signaling the host. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 1 + drivers/hv/hyperv_vmbus.h | 2 -- include/linux/hyperv.h | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index d02f1373dd98..fcf8a02dc0ea 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -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); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 12321b93a756..e5c586fab0e5 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -667,8 +667,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); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 66226ceade37..40fd608475f7 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1365,4 +1365,5 @@ 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); #endif /* _HYPERV_H */ -- cgit v1.2.3-71-gd317 From 687f32e6d9bd1d63c5e557e877809eb446f1a6e8 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 2 Apr 2016 17:59:50 -0700 Subject: Drivers: hv: vmbus: Move some ring buffer functions to hyperv.h In preparation for implementing APIs for in-place consumption of VMBUS packets, movve some ring buffer functionality into hyperv.h Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/ring_buffer.c | 55 ------------------------------------------------ include/linux/hyperv.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 8f518af3074d..dd255c9b9420 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -84,52 +84,6 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) 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 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; -} - /* Get the next write location for the specified ring buffer. */ static inline u32 hv_get_next_write_location(struct hv_ring_buffer_info *ring_info) @@ -180,15 +134,6 @@ hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, ring_info->ring_buffer->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) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 40fd608475f7..eb7c0b215ba4 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1366,4 +1366,58 @@ 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; +} + #endif /* _HYPERV_H */ -- cgit v1.2.3-71-gd317 From ab028db41ca9174caab7f9e3fc0a2e7f4a418410 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 2 Apr 2016 17:59:51 -0700 Subject: Drivers: hv: vmbus: Implement APIs to support "in place" consumption of vmbus packets Implement APIs for in-place consumption of vmbus packets. Currently, each packet is copied and processed one at a time and as part of processing each packet we potentially may signal the host (if it is waiting for room to produce a packet). These APIs help batched in-place processing of vmbus packets. We also optimize host signaling by having a separate API to signal the end of in-place consumption. With netvsc using these APIs, on an iperf run on average I see about 20X reduction in checks to signal the host. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/ring_buffer.c | 1 + include/linux/hyperv.h | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) (limited to 'include/linux') diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index dd255c9b9420..fe586bf74e17 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -132,6 +132,7 @@ 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 size of the ring buffer. */ diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index eb7c0b215ba4..590fee6a2e6f 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -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; }; /* @@ -1420,4 +1422,88 @@ static inline bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi) 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 */ -- cgit v1.2.3-71-gd317 From 97fb77dc87582300fa3c141b63699f853576cab1 Mon Sep 17 00:00:00 2001 From: Jake Oshins Date: Tue, 5 Apr 2016 10:22:51 -0700 Subject: drivers:hv: Make a function to free mmio regions through vmbus This patch introduces a function that reverses everything done by vmbus_allocate_mmio(). Existing code just called release_mem_region(). Future patches in this series require a more complex sequence of actions, so this function is introduced to wrap those actions. Signed-off-by: Jake Oshins Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 15 +++++++++++++++ include/linux/hyperv.h | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 799518b3cdc5..60553c156f90 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1187,6 +1187,21 @@ exit: } 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) +{ + release_mem_region(start, size); + +} +EXPORT_SYMBOL_GPL(vmbus_free_mmio); + /** * vmbus_cpu_number_to_vp_number() - Map CPU to VP. * @cpu_number: CPU number in Linux terms diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 590fee6a2e6f..b10954a66939 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1120,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); -- cgit v1.2.3-71-gd317 From 795ddd18d38f9762fbfefceab9aa16caef0cf431 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 24 Apr 2016 20:28:05 +0100 Subject: nvmem: core: remove regmap dependency nvmem uses regmap_raw_read/write apis to read/write data from providers, regmap raw apis stopped working with recent kernels which removed raw accessors on mmio bus. This resulted in broken nvmem for providers which are based on regmap mmio bus. This issue can be fixed temporarly by moving to other regmap apis, but we might hit same issue in future. Moving to interfaces based on read/write callbacks from providers would be more robust. This patch removes regmap dependency from nvmem and introduces read/write callbacks from the providers. Without this patch nvmem providers like qfprom based on regmap mmio bus would not work. Reported-by: Rajendra Nayak Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 1 - drivers/nvmem/core.c | 67 +++++++++++++++++++++++++----------------- include/linux/nvmem-provider.h | 10 +++++++ 3 files changed, 50 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 9c0c59d3b22b..15c58a5ff7ec 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -1,6 +1,5 @@ menuconfig NVMEM tristate "NVMEM Support" - select REGMAP help Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 0de3d878c439..bb4ea123547f 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -23,12 +23,10 @@ #include #include #include -#include #include 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; diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index a4fcc90b0f20..cd93416d762e 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -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; -- cgit v1.2.3-71-gd317 From 8e996a2874bbbed30e8dfe881453825fc6b7654e Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 3 May 2016 11:33:37 -0600 Subject: stm class: Support devices that override software assigned masters Some STM devices adjust software assigned master numbers depending on the trace source and its runtime state and whatnot. This patch adds a sysfs attribute to inform the trace-side software that master numbers assigned to software sources will not match those in the STP stream, so that, for example, master/channel allocation policy can be adjusted accordingly. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-stm | 10 ++++++++++ drivers/hwtracing/stm/core.c | 15 +++++++++++++++ include/linux/stm.h | 3 +++ 3 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-class-stm b/Documentation/ABI/testing/sysfs-class-stm index c9aa4f3fc9a7..77ed3da0f68e 100644 --- a/Documentation/ABI/testing/sysfs-class-stm +++ b/Documentation/ABI/testing/sysfs-class-stm @@ -12,3 +12,13 @@ KernelVersion: 4.3 Contact: Alexander Shishkin Description: Shows the number of channels per master on this STM device. + +What: /sys/class/stm//hw_override +Date: March 2016 +KernelVersion: 4.7 +Contact: Alexander Shishkin +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. diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 2591442e2c5b..ff31108b066f 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -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, }; diff --git a/include/linux/stm.h b/include/linux/stm.h index 1a79ed8e43da..8369d8a8cabd 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -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, -- cgit v1.2.3-71-gd317 From 237483aa5cf43105d148d3f03b29eed47c3e6cf9 Mon Sep 17 00:00:00 2001 From: Pratik Patel Date: Tue, 3 May 2016 11:33:40 -0600 Subject: coresight: stm: adding driver for CoreSight STM component This driver adds support for the STM CoreSight IP block, allowing any system compoment (HW or SW) to log and aggregate messages via a single entity. The CoreSight STM exposes an application defined number of channels called stimulus port. Configuration is done using entries in sysfs and channels made available to userspace via configfs. Signed-off-by: Pratik Patel Signed-off-by: Mathieu Poirier Reviewed-by: Michael Williams Signed-off-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-coresight-devices-stm | 53 ++ Documentation/trace/coresight.txt | 37 +- drivers/hwtracing/coresight/Kconfig | 11 + drivers/hwtracing/coresight/Makefile | 1 + drivers/hwtracing/coresight/coresight-stm.c | 920 +++++++++++++++++++++ include/linux/coresight-stm.h | 6 + include/uapi/linux/coresight-stm.h | 21 + 7 files changed, 1047 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-stm create mode 100644 drivers/hwtracing/coresight/coresight-stm.c create mode 100644 include/linux/coresight-stm.h create mode 100644 include/uapi/linux/coresight-stm.h (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm b/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm new file mode 100644 index 000000000000..1dffabe7f48d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm @@ -0,0 +1,53 @@ +What: /sys/bus/coresight/devices/.stm/enable_source +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +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/.stm/hwevent_enable +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Provides access to the HW event enable register, used in + conjunction with HW event bank select register. + +What: /sys/bus/coresight/devices/.stm/hwevent_select +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +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/.stm/port_enable +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Provides access to the stimulus port enable register + (STMSPER). Used in conjunction with "port_select" described + below. + +What: /sys/bus/coresight/devices/.stm/port_select +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Used to determine which bank of stimulus port bit in + register STMSPER (see above) apply to. + +What: /sys/bus/coresight/devices/.stm/status +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) List various control and status registers. The specific + layout and content is driver specific. + +What: /sys/bus/coresight/devices/.stm/traceid +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Holds the trace ID that will appear in the trace stream + coming from this trace entity. diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index 0a5c3290e732..a33c88cd5d1d 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -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 diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index db0541031c72..130cb2114059 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -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 diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 1d0e32c7dbe4..c6f84b57f52a 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.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 diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c new file mode 100644 index 000000000000..73be58a11e4f --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -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 + * + * Serious refactoring, code cleanup and upgrading to the Coresight upstream + * framework by Mathieu Poirier + * (C) 2015-2016 Mathieu Poirier + * + * Guaranteed timing and support for various packet type coming from the + * generic STM API by Chunyan Zhang + * (C) 2015-2016 Chunyan Zhang + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/include/linux/coresight-stm.h b/include/linux/coresight-stm.h new file mode 100644 index 000000000000..a978bb85599a --- /dev/null +++ b/include/linux/coresight-stm.h @@ -0,0 +1,6 @@ +#ifndef __LINUX_CORESIGHT_STM_H_ +#define __LINUX_CORESIGHT_STM_H_ + +#include + +#endif diff --git a/include/uapi/linux/coresight-stm.h b/include/uapi/linux/coresight-stm.h new file mode 100644 index 000000000000..7e4272cf1fb2 --- /dev/null +++ b/include/uapi/linux/coresight-stm.h @@ -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 -- cgit v1.2.3-71-gd317 From 18d28819809909c3f24bb72183a901c5e332a63d Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Tue, 3 May 2016 09:46:22 +0200 Subject: mcb: Correctly initialize the bus's device The mcb bus' device member wasn't correctly initialized and thus wasn't placed correctly into the driver model. Signed-off-by: Johannes Thumshirn Reviewed-by: Andreas Werner Tested-by: Andreas Werner Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-core.c | 19 ++++++++++++++++--- include/linux/mcb.h | 5 ++--- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c index a4be451074e5..1e336cc56751 100644 --- a/drivers/mcb/mcb-core.c +++ b/drivers/mcb/mcb-core.c @@ -187,6 +187,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 +195,26 @@ 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; + + device_initialize(&bus->dev); + bus->dev.parent = carrier; + bus->dev.bus = &mcb_bus_type; + + dev_set_name(&bus->dev, "mcb:%d", bus_nr); + rc = device_add(&bus->dev); + if (rc) + goto err_free; + return bus; +err_free: + kfree(bus); + return ERR_PTR(rc); } EXPORT_SYMBOL_GPL(mcb_alloc_bus); diff --git a/include/linux/mcb.h b/include/linux/mcb.h index ed06e15a36aa..3efafbca166d 100644 --- a/include/linux/mcb.h +++ b/include/linux/mcb.h @@ -21,13 +21,12 @@ 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 */ struct mcb_bus { - struct list_head children; struct device dev; struct device *carrier; int bus_nr; -- cgit v1.2.3-71-gd317 From 803f1ca60d5c0107adfbce4e2d70488598b03a80 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Tue, 3 May 2016 09:46:23 +0200 Subject: mcb: export bus information via sysfs Export information about the bus stored in the FPGA's header to userspace via sysfs, instead of hiding it in pr_debug()s from everyone. Signed-off-by: Johannes Thumshirn Reviewed-by: Andreas Werner Tested-by: Andreas Werner Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-mcb | 29 ++++++++++++++++ drivers/mcb/mcb-core.c | 60 +++++++++++++++++++++++++++++++++ drivers/mcb/mcb-internal.h | 1 - drivers/mcb/mcb-parse.c | 15 +++------ include/linux/mcb.h | 9 +++++ 5 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-mcb (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-mcb b/Documentation/ABI/testing/sysfs-bus-mcb new file mode 100644 index 000000000000..77947c509796 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-mcb @@ -0,0 +1,29 @@ +What: /sys/bus/mcb/devices/mcb:X +Date: March 2016 +KernelVersion: 4.7 +Contact: Johannes Thumshirn +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 +Description: The FPGA's revision number + +What: /sys/bus/mcb/devices/mcb:X/minor +Date: March 2016 +KernelVersion: 4.7 +Contact: Johannes Thumshirn +Description: The FPGA's minor number + +What: /sys/bus/mcb/devices/mcb:X/model +Date: March 2016 +KernelVersion: 4.7 +Contact: Johannes Thumshirn +Description: The FPGA's model number + +What: /sys/bus/mcb/devices/mcb:X/name +Date: March 2016 +KernelVersion: 4.7 +Contact: Johannes Thumshirn +Description: The FPGA's name diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c index 1e336cc56751..9ae4d15fc229 100644 --- a/drivers/mcb/mcb-core.c +++ b/drivers/mcb/mcb-core.c @@ -90,6 +90,60 @@ static void mcb_shutdown(struct device *dev) 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 @@ -205,6 +264,7 @@ struct mcb_bus *mcb_alloc_bus(struct device *carrier) device_initialize(&bus->dev); bus->dev.parent = carrier; bus->dev.bus = &mcb_bus_type; + bus->dev.type = &mcb_carrier_device_type; dev_set_name(&bus->dev, "mcb:%d", bus_nr); rc = device_add(&bus->dev); diff --git a/drivers/mcb/mcb-internal.h b/drivers/mcb/mcb-internal.h index fb7493dcfb79..5254e0285725 100644 --- a/drivers/mcb/mcb-internal.h +++ b/drivers/mcb/mcb-internal.h @@ -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 diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c index 004926955263..35f385b59221 100644 --- a/drivers/mcb/mcb-parse.c +++ b/drivers/mcb/mcb-parse.c @@ -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) { diff --git a/include/linux/mcb.h b/include/linux/mcb.h index 3efafbca166d..ead13d233a97 100644 --- a/include/linux/mcb.h +++ b/include/linux/mcb.h @@ -15,6 +15,8 @@ #include #include +#define CHAMELEON_FILENAME_LEN 12 + struct mcb_driver; struct mcb_device; @@ -25,11 +27,18 @@ struct mcb_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 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) -- cgit v1.2.3-71-gd317