Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Documentation/devicetree/bindings/mmc/mmc-card.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ patternProperties:
contains:
const: fixed-partitions

nvmem-layout:
$ref: /schemas/nvmem/layouts/nvmem-layout.yaml

required:
- compatible
- reg
Expand Down Expand Up @@ -86,6 +89,32 @@ examples:
read-only;
};
};

partitions-boot2 {
compatible = "fixed-partitions";

#address-cells = <1>;
#size-cells = <1>;

nvmem-layout {
compatible = "fixed-layout";

#address-cells = <1>;
#size-cells = <1>;

mac-addr@4400 {
compatible = "mac-base";
reg = <0x4400 0x6>;
#nvmem-cell-cells = <1>;
};

bd-addr@5400 {
compatible = "mac-base";
reg = <0x5400 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,13 @@ properties:
description:
boot firmware is incorrectly passing the address in big-endian order

nvmem-cells:
maxItems: 1
description:
Nvmem data cell that contains a 6 byte BD address with the most
significant byte first (big-endian).

nvmem-cell-names:
const: local-bd-address

additionalProperties: true
16 changes: 16 additions & 0 deletions Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ properties:

ieee80211-freq-limit: true

nvmem-cells:
minItems: 1
maxItems: 3
description:
References to nvmem cells for MAC address and/or calibration data.
Supported cell names are mac-address, calibration, and pre-calibration.

nvmem-cell-names:
minItems: 1
maxItems: 3
items:
enum:
- mac-address
- calibration
- pre-calibration

qcom,calibration-data:
$ref: /schemas/types.yaml#/definitions/uint8-array
description:
Expand Down
39 changes: 39 additions & 0 deletions arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,40 @@
no-sdio;
no-sd;

#address-cells = <1>;
#size-cells = <0>;

status = "okay";

card@0 {
compatible = "mmc-card";
reg = <0>;

partitions-boot1 {
compatible = "fixed-partitions";

#address-cells = <1>;
#size-cells = <1>;

nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;

wifi_mac_addr: mac-addr@4400 {
compatible = "mac-base";
reg = <0x4400 0x6>;
#nvmem-cell-cells = <1>;
};

bd_addr: bd-addr@5400 {
compatible = "mac-base";
reg = <0x5400 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
};
};

&spi5 {
Expand Down Expand Up @@ -512,6 +545,9 @@
vddch0-supply = <&pm4125_l22>;
enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>;
max-speed = <3000000>;

nvmem-cells = <&bd_addr 0>;
nvmem-cell-names = "local-bd-address";
};
};

Expand Down Expand Up @@ -557,6 +593,9 @@
qcom,ath10k-calibration-variant = "ArduinoImola";
firmware-name = "qcm2290";

nvmem-cells = <&wifi_mac_addr 0>;
nvmem-cell-names = "mac-address";

status = "okay";
};

Expand Down
9 changes: 9 additions & 0 deletions block/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
by falling back to the kernel crypto API when inline
encryption hardware is not present.

config BLK_NVMEM
bool "Block device NVMEM provider"
depends on OF
depends on NVMEM
help
Allow block devices (or partitions) to act as NVMEM providers,
typically used with eMMC to store MAC addresses or Wi-Fi
calibration data on embedded devices.

source "block/partitions/Kconfig"

config BLK_PM
Expand Down
1 change: 1 addition & 0 deletions block/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
blk-crypto-sysfs.o
obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
109 changes: 109 additions & 0 deletions block/blk-nvmem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* block device NVMEM provider
*
* Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* Useful on devices using a partition on an eMMC for MAC addresses or
* Wi-Fi calibration EEPROM data.
*/

#include <linux/file.h>
#include <linux/nvmem-provider.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/pagemap.h>
#include <linux/property.h>

#include "blk.h"

static int blk_nvmem_reg_read(void *priv, unsigned int from, void *val, size_t bytes)
{
blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
dev_t devt = (dev_t)(uintptr_t)priv;
size_t bytes_left = bytes;
loff_t pos = from;
int ret = 0;

struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
if (IS_ERR(bdev_file))
return PTR_ERR(bdev_file);

while (bytes_left) {
pgoff_t f_index = pos >> PAGE_SHIFT;
struct folio *folio;
size_t folio_off;
size_t to_read;

folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
if (IS_ERR(folio)) {
ret = PTR_ERR(folio);
break;
}

folio_off = offset_in_folio(folio, pos);
to_read = min(bytes_left, folio_size(folio) - folio_off);
memcpy_from_folio(val, folio, folio_off, to_read);
pos += to_read;
bytes_left -= to_read;
val += to_read;
folio_put(folio);
}

return ret;
}

void blk_nvmem_add(struct block_device *bdev)
{
struct device *dev = &bdev->bd_device;
struct nvmem_config config = {};

/* skip devices which do not have a device tree node */
if (!dev_of_node(dev))
return;

/* skip devices without an nvmem layout defined */
struct device_node *child __free(device_node) =
of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
if (!child)
return;

/*
* skip block device too large to be represented as NVMEM devices,
* the NVMEM reg_read callback uses an unsigned int offset
*/
if (bdev_nr_bytes(bdev) > UINT_MAX) {
dev_warn(dev, "block device too large to be an NVMEM provider\n");
return;
}

config.id = NVMEM_DEVID_NONE;
config.dev = dev;
config.name = dev_name(dev);
config.owner = THIS_MODULE;
config.priv = (void *)(uintptr_t)dev->devt;
config.reg_read = blk_nvmem_reg_read;
config.size = bdev_nr_bytes(bdev);
config.word_size = 1;
config.stride = 1;
config.read_only = true;
config.root_only = true;
config.ignore_wp = true;
config.of_node = to_of_node(dev->fwnode);

bdev->bd_nvmem = nvmem_register(&config);
if (IS_ERR(bdev->bd_nvmem)) {
dev_err_probe(dev, PTR_ERR(bdev->bd_nvmem),
"Failed to register NVMEM device\n");
bdev->bd_nvmem = NULL;
}
}

void blk_nvmem_del(struct block_device *bdev)
{
if (bdev->bd_nvmem)
nvmem_unregister(bdev->bd_nvmem);

bdev->bd_nvmem = NULL;
}
8 changes: 8 additions & 0 deletions block/blk.h
Original file line number Diff line number Diff line change
Expand Up @@ -778,4 +778,12 @@ static inline void blk_debugfs_unlock(struct request_queue *q,
memalloc_noio_restore(memflags);
}

#ifdef CONFIG_BLK_NVMEM
void blk_nvmem_add(struct block_device *bdev);
void blk_nvmem_del(struct block_device *bdev);
#else
static inline void blk_nvmem_add(struct block_device *bdev) {}
static inline void blk_nvmem_del(struct block_device *bdev) {}
#endif

#endif /* BLK_INTERNAL_H */
4 changes: 4 additions & 0 deletions block/genhd.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ static void add_disk_final(struct gendisk *disk)
*/
dev_set_uevent_suppress(ddev, 0);
disk_uevent(disk, KOBJ_ADD);

blk_nvmem_add(disk->part0);
}

blk_apply_bdi_limits(disk->bdi, &disk->queue->limits);
Expand Down Expand Up @@ -704,6 +706,8 @@ static void __del_gendisk(struct gendisk *disk)

disk_del_events(disk);

blk_nvmem_del(disk->part0);

/*
* Prevent new openers by unlinked the bdev inode.
*/
Expand Down
20 changes: 13 additions & 7 deletions block/partitions/of.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ static int validate_of_partition(struct device_node *np, int slot)
int a_cells = of_n_addr_cells(np);
int s_cells = of_n_size_cells(np);

/* Skip nodes without a reg property (e.g. nvmem-layout) */
if (!reg)
return 1;

/* Make sure reg len match the expected addr and size cells */
if (len / sizeof(*reg) != a_cells + s_cells)
return -EINVAL;
Expand Down Expand Up @@ -80,14 +84,15 @@ int of_partition(struct parsed_partitions *state)
slot = 1;
/* Validate parition offset and size */
for_each_child_of_node(partitions_np, np) {
if (validate_of_partition(np, slot)) {
int err = validate_of_partition(np, slot);

if (err < 0) {
of_node_put(np);
of_node_put(partitions_np);

return -1;
}

slot++;
if (!err)
slot++;
}

slot = 1;
Expand All @@ -97,9 +102,10 @@ int of_partition(struct parsed_partitions *state)
break;
}

add_of_partition(state, slot, np);

slot++;
if (of_property_present(np, "reg")) {
add_of_partition(state, slot, np);
slot++;
}
}

seq_buf_puts(&state->pp_buf, "\n");
Expand Down
5 changes: 4 additions & 1 deletion drivers/bluetooth/btqca.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,8 +721,11 @@ static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *co
}

bda = (struct hci_rp_read_bd_addr *)skb->data;
if (!bacmp(&bda->bdaddr, &config->bdaddr))
if (!bacmp(&bda->bdaddr, &config->bdaddr)) {
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
}

kfree_skb(skb);

Expand Down
3 changes: 3 additions & 0 deletions include/linux/blk_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ struct block_device {
int bd_writers;
#ifdef CONFIG_SECURITY
void *bd_security;
#endif
#ifdef CONFIG_BLK_NVMEM
struct nvmem_device *bd_nvmem;
#endif
/*
* keep this out-of-line as it's both big and not needed in the fast
Expand Down
1 change: 1 addition & 0 deletions include/linux/blkdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

struct module;
struct request_queue;
struct nvmem_device;
struct elevator_queue;
struct blk_trace;
struct request;
Expand Down
7 changes: 7 additions & 0 deletions include/linux/of_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct net_device;
extern int of_get_phy_mode(struct device_node *np, phy_interface_t *interface);
extern int of_get_mac_address(struct device_node *np, u8 *mac);
extern int of_get_mac_address_nvmem(struct device_node *np, u8 *mac);
int of_get_nvmem_eui48(struct device_node *np, const char *cell_name, u8 *addr);
int of_get_ethdev_address(struct device_node *np, struct net_device *dev);
extern struct net_device *of_find_net_device_by_node(struct device_node *np);
#else
Expand All @@ -34,6 +35,12 @@ static inline int of_get_mac_address_nvmem(struct device_node *np, u8 *mac)
return -ENODEV;
}

static inline int of_get_nvmem_eui48(struct device_node *np,
const char *cell_name, u8 *addr)
{
return -ENODEV;
}

static inline int of_get_ethdev_address(struct device_node *np, struct net_device *dev)
{
return -ENODEV;
Expand Down
Loading
Loading