[PATCH v8 6/9] drivers: perf: hisi: Add support for Hisilicon Djtag driver

Zhangshaokun zhangshaokun at hisilicon.com
Wed Jun 14 01:11:28 PDT 2017


Hi Mark,

On 2017/6/9 0:35, Mark Rutland wrote:
> Hi,
> 
> On Mon, May 22, 2017 at 08:48:32PM +0800, Shaokun Zhang wrote:
>> +/*
>> + * hisi_djtag_lock_v2: djtag lock to avoid djtag access conflict b/w kernel
>> + * and UEFI.
> 
> The mention of UEFI here worries me somewhat, and I have a number of
> questions specifically relating to how we interact with UEFI here.
> 
> When precisely does UEFI need to touch the djtag hardware? e.g. does
> this happen in runtime services? ... or completely asynchronously?
> 
> What does UEFI do with djtag when it holds the lock?
> 
> Are there other software agents (e.g. secure firmware) which try to
> take this lock?
> 
> Can you explain how the locking scheme works? e.g. is this an advisory
> software-only policy, or does the hardware prohibit accesses from other
> agents somehow?
> 
> What happens if the kernel takes the lock, but doesn't release it?
> 
> What happens if UEFI takes the lock, but doesn't release it?
> 
> Are there any points at which UEFI needs to hold the locks of several
> djtag units simultaneously?
> 
>> + * @reg_base:	djtag register base address
>> + *
>> + * Return - none.
>> + */
>> +static void hisi_djtag_lock_v2(void __iomem *regs_base)
>> +{
>> +	u32 rd, wr, mod_sel;
>> +
>> +	/* Continuously poll to ensure the djtag is free */
>> +	while (1) {
>> +		rd = readl(regs_base + SC_DJTAG_MSTR_CFG_EX);
>> +		mod_sel = ((rd >> DEBUG_MODULE_SEL_SHIFT_EX) & 0xFF);
>> +		if (mod_sel == SC_DJTAG_V2_UNLOCK) {
>> +			wr = ((rd & SC_DJTAG_V2_MODULE_SEL_MASK) |
>> +			      (SC_DJTAG_V2_KERNEL_LOCK <<
>> +			       DEBUG_MODULE_SEL_SHIFT_EX));
>> +			writel(wr, regs_base + SC_DJTAG_MSTR_CFG_EX);
>> +			udelay(10); /* 10us */
>> +
>> +			rd = readl(regs_base + SC_DJTAG_MSTR_CFG_EX);
>> +			mod_sel = ((rd >> DEBUG_MODULE_SEL_SHIFT_EX) & 0xFF);
>> +			if (mod_sel == SC_DJTAG_V2_KERNEL_LOCK)
>> +				break;
>> +		}
>> +		udelay(10); /* 10us */
>> +	}
>> +}
>> +
>> +/*
>> + * hisi_djtag_unlock_v2: djtag unlock
>> + * @reg_base:	djtag register base address
>> + *
>> + * Return - none.
>> + */
>> +static void hisi_djtag_unlock_v2(void __iomem *regs_base)
>> +{
>> +	u32 rd, wr;
>> +
>> +	rd = readl(regs_base + SC_DJTAG_MSTR_CFG_EX);
>> +
>> +	wr = ((rd & SC_DJTAG_V2_MODULE_SEL_MASK) |
>> +	      (SC_DJTAG_V2_UNLOCK << DEBUG_MODULE_SEL_SHIFT_EX));
>> +	/*
>> +	 * Release djtag module by writing to module
>> +	 * selection bits of DJTAG_MSTR_CFG register
>> +	 */
>> +	writel(wr, regs_base + SC_DJTAG_MSTR_CFG_EX);
>> +}
> 
> [...]
> 
>> +static void hisi_djtag_prepare_v2(void __iomem *regs_base, u32 offset,
>> +				  u32 mod_sel, u32 mod_mask)
>> +{
>> +	/* djtag mster enable */
> 
> s/mster/master/ ?
> 

True, this is one of my typo.

> [...]
> 
>> +static ssize_t show_modalias(struct device *dev,
>> +			     struct device_attribute *attr, char *buf)
>> +{
>> +	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
>> +
>> +	return sprintf(buf, "%s%s\n", MODULE_PREFIX, dev_name(&client->dev));
>> +}
>> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
>> +
>> +static struct attribute *hisi_djtag_dev_attrs[] = {
>> +	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
>> +	&dev_attr_modalias.attr,
>> +	NULL
>> +};
>> +ATTRIBUTE_GROUPS(hisi_djtag_dev);
>> +
>> +static struct device_type hisi_djtag_client_type = {
>> +	.groups	= hisi_djtag_dev_groups,
>> +};
> 
> Can you elaborate on what this sysfs stuff is for?
> 

Yes. It just displays the name of djtag device node and is useless. We will delete
it.

>> +static int hisi_djtag_device_match(struct device *dev,
>> +				   struct device_driver *drv)
>> +{
>> +	/* Attempt an OF style match */
>> +	if (of_driver_match_device(dev, drv))
>> +		return true;
>> +
>> +#ifdef CONFIG_ACPI
>> +	if (acpi_driver_match_device(dev, drv))
>> +		return true;
>> +#endif
> 
> You can drop the ifdef here. When CONFIG_ACPI is not selected,
> acpi_driver_match_device() is a static inline that always returns false.
> 

Agree. We shall simplify it.

>> +	return false;
>> +}
> 
> [...]
> 
>> +static int hisi_djtag_set_client_name(struct hisi_djtag_client *client,
>> +				      const char *device_name)
>> +{
>> +	char name[DJTAG_CLIENT_NAME_LEN];
>> +	int id;
>> +
>> +	id = hisi_djtag_get_client_id(client);
>> +	if (id < 0)
>> +		return id;
>> +
>> +	client->id = id;
>> +	snprintf(name, DJTAG_CLIENT_NAME_LEN, "%s%s_%d", DJTAG_PREFIX,
>> +		 device_name, client->id);
>> +	dev_set_name(&client->dev, "%s", name);
>> +
>> +	return 0;
>> +}
> 
> Given dev_set_name() takes a fmt string, you don't need the temporary
> name here, and can have dev_set_name() handle that directly, e.g.
> 
> 	err = dev_set_name(&client->dev, %s%s_%d",
>   			   DJTAG_PREFIX, device_name, client->id);
> 	if (err)
> 		return err;
> 
> 
> Given DJTAG_PREFIX is only used here, it would be better to fold it into
> the format string, also.
> 

Agree, we will modify it.

>> +
>> +static int hisi_djtag_new_of_device(struct hisi_djtag_host *host,
>> +				    struct device_node *node)
>> +{
>> +	struct hisi_djtag_client *client;
>> +	int ret;
>> +
>> +	client = hisi_djtag_client_alloc(host);
>> +	if (!client) {
>> +		dev_err(&host->dev, "DT: Client alloc fail!\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	client->dev.of_node = of_node_get(node);
>> +
>> +	ret = hisi_djtag_set_client_name(client, node->name);
> 
> I don't think it's a good idea to take the name directly from the DT.
> 
> Can we please use a standard name, looked up based on the compatible
> string? Then we can also use the same names for ACPI. Where there are
> multiple instances, we can use the module-id too.
> 
> [...]
> 

Ok, shall modify it.

>> +static void djtag_register_devices(struct hisi_djtag_host *host)
>> +{
>> +	struct device_node *node;
>> +
>> +	if (host->of_node) {
>> +		for_each_available_child_of_node(host->of_node, node) {
>> +			if (of_node_test_and_set_flag(node, OF_POPULATED))
>> +				continue;
>> +			if (hisi_djtag_new_of_device(host, node))
>> +				break;
> 
> Why do we stop, rather than rolling back and freeing what we allocated?
> 
> Either that, or we should return an error code, such that a higher level
> can choose to do so.
> 

We need to improve the error handling and rollback.

>> +		}
>> +	} else if (host->acpi_handle) {
>> +		acpi_handle handle = host->acpi_handle;
>> +		acpi_status status;
>> +
>> +		status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
>> +					     djtag_add_new_acpi_device, NULL,
>> +					     host, NULL);
>> +		if (ACPI_FAILURE(status)) {
>> +			dev_err(&host->dev,
>> +				"ACPI: Fail to read client devices!\n");
>> +			return;
> 
> Likewise, why aren't we rolling back?
> 
> [...]
> 
>> +#define DJTAG_CLIENT_NAME_LEN 32
> 
> I beleive this can go.
> 

ok.

thanks
Shaokun

> Thanks,
> Mark.
> 
> .
> 




More information about the linux-arm-kernel mailing list