summaryrefslogtreecommitdiff
path: root/drivers/greybus
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/greybus')
-rw-r--r--drivers/greybus/Kconfig2
-rw-r--r--drivers/greybus/bundle.c2
-rw-r--r--drivers/greybus/control.c2
-rw-r--r--drivers/greybus/core.c37
-rw-r--r--drivers/greybus/es2.c11
-rw-r--r--drivers/greybus/gb-beagleplay.c666
-rw-r--r--drivers/greybus/hd.c18
-rw-r--r--drivers/greybus/interface.c14
-rw-r--r--drivers/greybus/module.c2
-rw-r--r--drivers/greybus/operation.c5
-rw-r--r--drivers/greybus/svc.c2
11 files changed, 715 insertions, 46 deletions
diff --git a/drivers/greybus/Kconfig b/drivers/greybus/Kconfig
index ab81ceceb337..c3f056d28b01 100644
--- a/drivers/greybus/Kconfig
+++ b/drivers/greybus/Kconfig
@@ -21,6 +21,8 @@ config GREYBUS_BEAGLEPLAY
tristate "Greybus BeaglePlay driver"
depends on SERIAL_DEV_BUS
select CRC_CCITT
+ select FW_LOADER
+ select FW_UPLOAD
help
Select this option if you have a BeaglePlay where CC1352
co-processor acts as Greybus SVC.
diff --git a/drivers/greybus/bundle.c b/drivers/greybus/bundle.c
index 84660729538b..a6e1cca06172 100644
--- a/drivers/greybus/bundle.c
+++ b/drivers/greybus/bundle.c
@@ -166,7 +166,7 @@ static const struct dev_pm_ops gb_bundle_pm_ops = {
SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle)
};
-struct device_type greybus_bundle_type = {
+const struct device_type greybus_bundle_type = {
.name = "greybus_bundle",
.release = gb_bundle_release,
.pm = &gb_bundle_pm_ops,
diff --git a/drivers/greybus/control.c b/drivers/greybus/control.c
index 359a25841973..b5cf49d09df2 100644
--- a/drivers/greybus/control.c
+++ b/drivers/greybus/control.c
@@ -436,7 +436,7 @@ static void gb_control_release(struct device *dev)
kfree(control);
}
-struct device_type greybus_control_type = {
+const struct device_type greybus_control_type = {
.name = "greybus_control",
.release = gb_control_release,
};
diff --git a/drivers/greybus/core.c b/drivers/greybus/core.c
index 5714be740470..313eb65cf703 100644
--- a/drivers/greybus/core.c
+++ b/drivers/greybus/core.c
@@ -27,6 +27,36 @@ int greybus_disabled(void)
}
EXPORT_SYMBOL_GPL(greybus_disabled);
+static int is_gb_host_device(const struct device *dev)
+{
+ return dev->type == &greybus_hd_type;
+}
+
+static int is_gb_module(const struct device *dev)
+{
+ return dev->type == &greybus_module_type;
+}
+
+static int is_gb_interface(const struct device *dev)
+{
+ return dev->type == &greybus_interface_type;
+}
+
+static int is_gb_control(const struct device *dev)
+{
+ return dev->type == &greybus_control_type;
+}
+
+static int is_gb_bundle(const struct device *dev)
+{
+ return dev->type == &greybus_bundle_type;
+}
+
+static int is_gb_svc(const struct device *dev)
+{
+ return dev->type == &greybus_svc_type;
+}
+
static bool greybus_match_one_id(struct gb_bundle *bundle,
const struct greybus_bundle_id *id)
{
@@ -60,9 +90,9 @@ greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id)
return NULL;
}
-static int greybus_match_device(struct device *dev, struct device_driver *drv)
+static int greybus_match_device(struct device *dev, const struct device_driver *drv)
{
- struct greybus_driver *driver = to_greybus_driver(drv);
+ const struct greybus_driver *driver = to_greybus_driver(drv);
struct gb_bundle *bundle;
const struct greybus_bundle_id *id;
@@ -155,7 +185,7 @@ static void greybus_shutdown(struct device *dev)
}
}
-struct bus_type greybus_bus_type = {
+const struct bus_type greybus_bus_type = {
.name = "greybus",
.match = greybus_match_device,
.uevent = greybus_uevent,
@@ -345,5 +375,6 @@ static void __exit gb_exit(void)
tracepoint_synchronize_unregister();
}
module_exit(gb_exit);
+MODULE_DESCRIPTION("Greybus core driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
diff --git a/drivers/greybus/es2.c b/drivers/greybus/es2.c
index e89cca015095..7630a36ecf81 100644
--- a/drivers/greybus/es2.c
+++ b/drivers/greybus/es2.c
@@ -12,7 +12,7 @@
#include <linux/debugfs.h>
#include <linux/list.h>
#include <linux/greybus.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "arpc.h"
#include "greybus_trace.h"
@@ -513,16 +513,16 @@ static int es2_cport_allocate(struct gb_host_device *hd, int cport_id,
if (cport_id < 0) {
ida_start = 0;
- ida_end = hd->num_cports;
+ ida_end = hd->num_cports - 1;
} else if (cport_id < hd->num_cports) {
ida_start = cport_id;
- ida_end = cport_id + 1;
+ ida_end = cport_id;
} else {
dev_err(&hd->dev, "cport %d not available\n", cport_id);
return -EINVAL;
}
- return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
+ return ida_alloc_range(id_map, ida_start, ida_end, GFP_KERNEL);
}
static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
@@ -535,7 +535,7 @@ static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
return;
}
- ida_simple_remove(&hd->cport_id_map, cport_id);
+ ida_free(&hd->cport_id_map, cport_id);
}
static int cport_enable(struct gb_host_device *hd, u16 cport_id,
@@ -1456,5 +1456,6 @@ static struct usb_driver es2_ap_driver = {
module_usb_driver(es2_ap_driver);
+MODULE_DESCRIPTION("Greybus AP USB driver for ES2 controller chips");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c
index c3e90025064b..da31f1131afc 100644
--- a/drivers/greybus/gb-beagleplay.c
+++ b/drivers/greybus/gb-beagleplay.c
@@ -6,21 +6,19 @@
* Copyright (c) 2023 BeagleBoard.org Foundation
*/
-#include <linux/gfp.h>
+#include <linux/unaligned.h>
+#include <linux/crc32.h>
+#include <linux/gpio/consumer.h>
+#include <linux/firmware.h>
#include <linux/greybus.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/printk.h>
#include <linux/serdev.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/greybus/hd.h>
-#include <linux/init.h>
-#include <linux/device.h>
#include <linux/crc-ccitt.h>
#include <linux/circ_buf.h>
-#include <linux/types.h>
-#include <linux/workqueue.h>
+
+#define CC1352_FIRMWARE_SIZE (704 * 1024)
+#define CC1352_BOOTLOADER_TIMEOUT 2000
+#define CC1352_BOOTLOADER_ACK 0xcc
+#define CC1352_BOOTLOADER_NACK 0x33
#define RX_HDLC_PAYLOAD 256
#define CRC_LEN 2
@@ -57,6 +55,17 @@
* @rx_buffer_len: length of receive buffer filled.
* @rx_buffer: hdlc frame receive buffer
* @rx_in_esc: hdlc rx flag to indicate ESC frame
+ *
+ * @fwl: underlying firmware upload device
+ * @bootloader_backdoor_gpio: cc1352p7 boot gpio
+ * @rst_gpio: cc1352p7 reset gpio
+ * @flashing_mode: flag to indicate that flashing is currently in progress
+ * @fwl_ack_com: completion to signal an Ack/Nack
+ * @fwl_ack: Ack/Nack byte received
+ * @fwl_cmd_response_com: completion to signal a bootloader command response
+ * @fwl_cmd_response: bootloader command response data
+ * @fwl_crc32: crc32 of firmware to flash
+ * @fwl_reset_addr: flag to indicate if we need to send COMMAND_DOWNLOAD again
*/
struct gb_beagleplay {
struct serdev_device *sd;
@@ -72,6 +81,17 @@ struct gb_beagleplay {
u16 rx_buffer_len;
bool rx_in_esc;
u8 rx_buffer[MAX_RX_HDLC];
+
+ struct fw_upload *fwl;
+ struct gpio_desc *bootloader_backdoor_gpio;
+ struct gpio_desc *rst_gpio;
+ bool flashing_mode;
+ struct completion fwl_ack_com;
+ u8 fwl_ack;
+ struct completion fwl_cmd_response_com;
+ u32 fwl_cmd_response;
+ u32 fwl_crc32;
+ bool fwl_reset_addr;
};
/**
@@ -100,6 +120,87 @@ struct hdlc_greybus_frame {
u8 payload[];
} __packed;
+/**
+ * enum cc1352_bootloader_cmd: CC1352 Bootloader Commands
+ *
+ * @COMMAND_DOWNLOAD: Prepares flash programming
+ * @COMMAND_GET_STATUS: Returns the status of the last command that was issued
+ * @COMMAND_SEND_DATA: Transfers data and programs flash
+ * @COMMAND_RESET: Performs a system reset
+ * @COMMAND_CRC32: Calculates CRC32 over a specified memory area
+ * @COMMAND_BANK_ERASE: Performs an erase of all of the customer-accessible
+ * flash sectors not protected by FCFG1 and CCFG
+ * writeprotect bits.
+ *
+ * CC1352 Bootloader serial bus commands
+ */
+enum cc1352_bootloader_cmd {
+ COMMAND_DOWNLOAD = 0x21,
+ COMMAND_GET_STATUS = 0x23,
+ COMMAND_SEND_DATA = 0x24,
+ COMMAND_RESET = 0x25,
+ COMMAND_CRC32 = 0x27,
+ COMMAND_BANK_ERASE = 0x2c,
+};
+
+/**
+ * enum cc1352_bootloader_status: CC1352 Bootloader COMMAND_GET_STATUS response
+ *
+ * @COMMAND_RET_SUCCESS: Status for successful command
+ * @COMMAND_RET_UNKNOWN_CMD: Status for unknown command
+ * @COMMAND_RET_INVALID_CMD: Status for invalid command (in other words,
+ * incorrect packet size)
+ * @COMMAND_RET_INVALID_ADR: Status for invalid input address
+ * @COMMAND_RET_FLASH_FAIL: Status for failing flash erase or program operation
+ */
+enum cc1352_bootloader_status {
+ COMMAND_RET_SUCCESS = 0x40,
+ COMMAND_RET_UNKNOWN_CMD = 0x41,
+ COMMAND_RET_INVALID_CMD = 0x42,
+ COMMAND_RET_INVALID_ADR = 0x43,
+ COMMAND_RET_FLASH_FAIL = 0x44,
+};
+
+/**
+ * struct cc1352_bootloader_packet: CC1352 Bootloader Request Packet
+ *
+ * @len: length of packet + optional request data
+ * @checksum: 8-bit checksum excluding len
+ * @cmd: bootloader command
+ */
+struct cc1352_bootloader_packet {
+ u8 len;
+ u8 checksum;
+ u8 cmd;
+} __packed;
+
+#define CC1352_BOOTLOADER_PKT_MAX_SIZE \
+ (U8_MAX - sizeof(struct cc1352_bootloader_packet))
+
+/**
+ * struct cc1352_bootloader_download_cmd_data: CC1352 Bootloader COMMAND_DOWNLOAD request data
+ *
+ * @addr: address to start programming data into
+ * @size: size of data that will be sent
+ */
+struct cc1352_bootloader_download_cmd_data {
+ __be32 addr;
+ __be32 size;
+} __packed;
+
+/**
+ * struct cc1352_bootloader_crc32_cmd_data: CC1352 Bootloader COMMAND_CRC32 request data
+ *
+ * @addr: address where crc32 calculation starts
+ * @size: number of bytes comprised by crc32 calculation
+ * @read_repeat: number of read repeats for each data location
+ */
+struct cc1352_bootloader_crc32_cmd_data {
+ __be32 addr;
+ __be32 size;
+ __be32 read_repeat;
+} __packed;
+
static void hdlc_rx_greybus_frame(struct gb_beagleplay *bg, u8 *buf, u16 len)
{
struct hdlc_greybus_frame *gb_frame = (struct hdlc_greybus_frame *)buf;
@@ -271,7 +372,7 @@ static void hdlc_rx_frame(struct gb_beagleplay *bg)
}
}
-static ssize_t hdlc_rx(struct gb_beagleplay *bg, const u8 *data, size_t count)
+static size_t hdlc_rx(struct gb_beagleplay *bg, const u8 *data, size_t count)
{
size_t i;
u8 c;
@@ -331,11 +432,135 @@ static void hdlc_deinit(struct gb_beagleplay *bg)
flush_work(&bg->tx_work);
}
-static ssize_t gb_tty_receive(struct serdev_device *sd, const u8 *data,
- size_t count)
+/**
+ * csum8: Calculate 8-bit checksum on data
+ *
+ * @data: bytes to calculate 8-bit checksum of
+ * @size: number of bytes
+ * @base: starting value for checksum
+ */
+static u8 csum8(const u8 *data, size_t size, u8 base)
+{
+ size_t i;
+ u8 sum = base;
+
+ for (i = 0; i < size; ++i)
+ sum += data[i];
+
+ return sum;
+}
+
+static void cc1352_bootloader_send_ack(struct gb_beagleplay *bg)
+{
+ static const u8 ack[] = { 0x00, CC1352_BOOTLOADER_ACK };
+
+ serdev_device_write_buf(bg->sd, ack, sizeof(ack));
+}
+
+static void cc1352_bootloader_send_nack(struct gb_beagleplay *bg)
+{
+ static const u8 nack[] = { 0x00, CC1352_BOOTLOADER_NACK };
+
+ serdev_device_write_buf(bg->sd, nack, sizeof(nack));
+}
+
+/**
+ * cc1352_bootloader_pkt_rx: Process a CC1352 Bootloader Packet
+ *
+ * @bg: beagleplay greybus driver
+ * @data: packet buffer
+ * @count: packet buffer size
+ *
+ * @return: number of bytes processed
+ *
+ * Here are the steps to successfully receive a packet from cc1352 bootloader
+ * according to the docs:
+ * 1. Wait for nonzero data to be returned from the device. This is important
+ * as the device may send zero bytes between a sent and a received data
+ * packet. The first nonzero byte received is the size of the packet that is
+ * being received.
+ * 2. Read the next byte, which is the checksum for the packet.
+ * 3. Read the data bytes from the device. During the data phase, packet size
+ * minus 2 bytes is sent.
+ * 4. Calculate the checksum of the data bytes and verify it matches the
+ * checksum received in the packet.
+ * 5. Send an acknowledge byte or a not-acknowledge byte to the device to
+ * indicate the successful or unsuccessful reception of the packet.
+ */
+static int cc1352_bootloader_pkt_rx(struct gb_beagleplay *bg, const u8 *data,
+ size_t count)
+{
+ bool is_valid = false;
+
+ switch (data[0]) {
+ /* Skip 0x00 bytes. */
+ case 0x00:
+ return 1;
+ case CC1352_BOOTLOADER_ACK:
+ case CC1352_BOOTLOADER_NACK:
+ WRITE_ONCE(bg->fwl_ack, data[0]);
+ complete(&bg->fwl_ack_com);
+ return 1;
+ case 3:
+ if (count < 3)
+ return 0;
+ is_valid = data[1] == data[2];
+ WRITE_ONCE(bg->fwl_cmd_response, (u32)data[2]);
+ break;
+ case 6:
+ if (count < 6)
+ return 0;
+ is_valid = csum8(&data[2], sizeof(__be32), 0) == data[1];
+ WRITE_ONCE(bg->fwl_cmd_response, get_unaligned_be32(&data[2]));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (is_valid) {
+ cc1352_bootloader_send_ack(bg);
+ complete(&bg->fwl_cmd_response_com);
+ } else {
+ dev_warn(&bg->sd->dev,
+ "Dropping bootloader packet with invalid checksum");
+ cc1352_bootloader_send_nack(bg);
+ }
+
+ return data[0];
+}
+
+static size_t cc1352_bootloader_rx(struct gb_beagleplay *bg, const u8 *data,
+ size_t count)
+{
+ int ret;
+ size_t off = 0;
+
+ memcpy(bg->rx_buffer + bg->rx_buffer_len, data, count);
+ bg->rx_buffer_len += count;
+
+ do {
+ ret = cc1352_bootloader_pkt_rx(bg, bg->rx_buffer + off,
+ bg->rx_buffer_len - off);
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, ret,
+ "Invalid Packet");
+ off += ret;
+ } while (ret > 0 && off < count);
+
+ bg->rx_buffer_len -= off;
+ memmove(bg->rx_buffer, bg->rx_buffer + off, bg->rx_buffer_len);
+
+ return count;
+}
+
+static size_t gb_tty_receive(struct serdev_device *sd, const u8 *data,
+ size_t count)
{
struct gb_beagleplay *bg = serdev_device_get_drvdata(sd);
+ if (READ_ONCE(bg->flashing_mode))
+ return cc1352_bootloader_rx(bg, data, count);
+
return hdlc_rx(bg, data, count);
}
@@ -343,7 +568,8 @@ static void gb_tty_wakeup(struct serdev_device *serdev)
{
struct gb_beagleplay *bg = serdev_device_get_drvdata(serdev);
- schedule_work(&bg->tx_work);
+ if (!READ_ONCE(bg->flashing_mode))
+ schedule_work(&bg->tx_work);
}
static struct serdev_device_ops gb_beagleplay_ops = {
@@ -412,6 +638,195 @@ static void gb_beagleplay_stop_svc(struct gb_beagleplay *bg)
hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1);
}
+static int cc1352_bootloader_wait_for_ack(struct gb_beagleplay *bg)
+{
+ int ret;
+
+ ret = wait_for_completion_timeout(
+ &bg->fwl_ack_com, msecs_to_jiffies(CC1352_BOOTLOADER_TIMEOUT));
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, ret,
+ "Failed to acquire ack semaphore");
+
+ switch (READ_ONCE(bg->fwl_ack)) {
+ case CC1352_BOOTLOADER_ACK:
+ return 0;
+ case CC1352_BOOTLOADER_NACK:
+ return -EAGAIN;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cc1352_bootloader_sync(struct gb_beagleplay *bg)
+{
+ static const u8 sync_bytes[] = { 0x55, 0x55 };
+
+ serdev_device_write_buf(bg->sd, sync_bytes, sizeof(sync_bytes));
+ return cc1352_bootloader_wait_for_ack(bg);
+}
+
+static int cc1352_bootloader_get_status(struct gb_beagleplay *bg)
+{
+ int ret;
+ static const struct cc1352_bootloader_packet pkt = {
+ .len = sizeof(pkt),
+ .checksum = COMMAND_GET_STATUS,
+ .cmd = COMMAND_GET_STATUS
+ };
+
+ serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt));
+ ret = cc1352_bootloader_wait_for_ack(bg);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(
+ &bg->fwl_cmd_response_com,
+ msecs_to_jiffies(CC1352_BOOTLOADER_TIMEOUT));
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, ret,
+ "Failed to acquire last status semaphore");
+
+ switch (READ_ONCE(bg->fwl_cmd_response)) {
+ case COMMAND_RET_SUCCESS:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cc1352_bootloader_erase(struct gb_beagleplay *bg)
+{
+ int ret;
+ static const struct cc1352_bootloader_packet pkt = {
+ .len = sizeof(pkt),
+ .checksum = COMMAND_BANK_ERASE,
+ .cmd = COMMAND_BANK_ERASE
+ };
+
+ serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt));
+
+ ret = cc1352_bootloader_wait_for_ack(bg);
+ if (ret < 0)
+ return ret;
+
+ return cc1352_bootloader_get_status(bg);
+}
+
+static int cc1352_bootloader_reset(struct gb_beagleplay *bg)
+{
+ static const struct cc1352_bootloader_packet pkt = {
+ .len = sizeof(pkt),
+ .checksum = COMMAND_RESET,
+ .cmd = COMMAND_RESET
+ };
+
+ serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt));
+
+ return cc1352_bootloader_wait_for_ack(bg);
+}
+
+/**
+ * cc1352_bootloader_empty_pkt: Calculate the number of empty bytes in the current packet
+ *
+ * @data: packet bytes array to check
+ * @size: number of bytes in array
+ */
+static size_t cc1352_bootloader_empty_pkt(const u8 *data, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size && data[i] == 0xff; ++i)
+ continue;
+
+ return i;
+}
+
+static int cc1352_bootloader_crc32(struct gb_beagleplay *bg, u32 *crc32)
+{
+ int ret;
+ static const struct cc1352_bootloader_crc32_cmd_data cmd_data = {
+ .addr = 0, .size = cpu_to_be32(704 * 1024), .read_repeat = 0
+ };
+ const struct cc1352_bootloader_packet pkt = {
+ .len = sizeof(pkt) + sizeof(cmd_data),
+ .checksum = csum8((const void *)&cmd_data, sizeof(cmd_data),
+ COMMAND_CRC32),
+ .cmd = COMMAND_CRC32
+ };
+
+ serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt));
+ serdev_device_write_buf(bg->sd, (const u8 *)&cmd_data,
+ sizeof(cmd_data));
+
+ ret = cc1352_bootloader_wait_for_ack(bg);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(
+ &bg->fwl_cmd_response_com,
+ msecs_to_jiffies(CC1352_BOOTLOADER_TIMEOUT));
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, ret,
+ "Failed to acquire last status semaphore");
+
+ *crc32 = READ_ONCE(bg->fwl_cmd_response);
+
+ return 0;
+}
+
+static int cc1352_bootloader_download(struct gb_beagleplay *bg, u32 size,
+ u32 addr)
+{
+ int ret;
+ const struct cc1352_bootloader_download_cmd_data cmd_data = {
+ .addr = cpu_to_be32(addr),
+ .size = cpu_to_be32(size),
+ };
+ const struct cc1352_bootloader_packet pkt = {
+ .len = sizeof(pkt) + sizeof(cmd_data),
+ .checksum = csum8((const void *)&cmd_data, sizeof(cmd_data),
+ COMMAND_DOWNLOAD),
+ .cmd = COMMAND_DOWNLOAD
+ };
+
+ serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt));
+ serdev_device_write_buf(bg->sd, (const u8 *)&cmd_data,
+ sizeof(cmd_data));
+
+ ret = cc1352_bootloader_wait_for_ack(bg);
+ if (ret < 0)
+ return ret;
+
+ return cc1352_bootloader_get_status(bg);
+}
+
+static int cc1352_bootloader_send_data(struct gb_beagleplay *bg, const u8 *data,
+ size_t size)
+{
+ int ret, rem = min(size, CC1352_BOOTLOADER_PKT_MAX_SIZE);
+ const struct cc1352_bootloader_packet pkt = {
+ .len = sizeof(pkt) + rem,
+ .checksum = csum8(data, rem, COMMAND_SEND_DATA),
+ .cmd = COMMAND_SEND_DATA
+ };
+
+ serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt));
+ serdev_device_write_buf(bg->sd, data, rem);
+
+ ret = cc1352_bootloader_wait_for_ack(bg);
+ if (ret < 0)
+ return ret;
+
+ ret = cc1352_bootloader_get_status(bg);
+ if (ret < 0)
+ return ret;
+
+ return rem;
+}
+
static void gb_greybus_deinit(struct gb_beagleplay *bg)
{
gb_hd_del(bg->gb_hd);
@@ -442,6 +857,159 @@ free_gb_hd:
return ret;
}
+static enum fw_upload_err cc1352_prepare(struct fw_upload *fw_upload,
+ const u8 *data, u32 size)
+{
+ int ret;
+ u32 curr_crc32;
+ struct gb_beagleplay *bg = fw_upload->dd_handle;
+
+ dev_info(&bg->sd->dev, "CC1352 Start Flashing...");
+
+ if (size != CC1352_FIRMWARE_SIZE)
+ return FW_UPLOAD_ERR_INVALID_SIZE;
+
+ /* Might involve network calls */
+ gb_greybus_deinit(bg);
+ msleep(5 * MSEC_PER_SEC);
+
+ gb_beagleplay_stop_svc(bg);
+ msleep(200);
+ flush_work(&bg->tx_work);
+
+ serdev_device_wait_until_sent(bg->sd, CC1352_BOOTLOADER_TIMEOUT);
+
+ WRITE_ONCE(bg->flashing_mode, true);
+
+ gpiod_direction_output(bg->bootloader_backdoor_gpio, 0);
+ gpiod_direction_output(bg->rst_gpio, 0);
+ msleep(200);
+
+ gpiod_set_value(bg->rst_gpio, 1);
+ msleep(200);
+
+ gpiod_set_value(bg->bootloader_backdoor_gpio, 1);
+ msleep(200);
+
+ gpiod_direction_input(bg->bootloader_backdoor_gpio);
+ gpiod_direction_input(bg->rst_gpio);
+
+ ret = cc1352_bootloader_sync(bg);
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR,
+ "Failed to sync");
+
+ ret = cc1352_bootloader_crc32(bg, &curr_crc32);
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR,
+ "Failed to fetch crc32");
+
+ bg->fwl_crc32 = crc32(0xffffffff, data, size) ^ 0xffffffff;
+
+ /* Check if attempting to reflash same firmware */
+ if (bg->fwl_crc32 == curr_crc32) {
+ dev_warn(&bg->sd->dev, "Skipping reflashing same image");
+ cc1352_bootloader_reset(bg);
+ WRITE_ONCE(bg->flashing_mode, false);
+ msleep(200);
+ if (gb_greybus_init(bg) < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR,
+ "Failed to initialize greybus");
+ gb_beagleplay_start_svc(bg);
+ return FW_UPLOAD_ERR_FW_INVALID;
+ }
+
+ ret = cc1352_bootloader_erase(bg);
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR,
+ "Failed to erase");
+
+ bg->fwl_reset_addr = true;
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void cc1352_cleanup(struct fw_upload *fw_upload)
+{
+ struct gb_beagleplay *bg = fw_upload->dd_handle;
+
+ WRITE_ONCE(bg->flashing_mode, false);
+}
+
+static enum fw_upload_err cc1352_write(struct fw_upload *fw_upload,
+ const u8 *data, u32 offset, u32 size,
+ u32 *written)
+{
+ int ret;
+ size_t empty_bytes;
+ struct gb_beagleplay *bg = fw_upload->dd_handle;
+
+ /* Skip 0xff packets. Significant performance improvement */
+ empty_bytes = cc1352_bootloader_empty_pkt(data + offset, size);
+ if (empty_bytes >= CC1352_BOOTLOADER_PKT_MAX_SIZE) {
+ bg->fwl_reset_addr = true;
+ *written = empty_bytes;
+ return FW_UPLOAD_ERR_NONE;
+ }
+
+ if (bg->fwl_reset_addr) {
+ ret = cc1352_bootloader_download(bg, size, offset);
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev,
+ FW_UPLOAD_ERR_HW_ERROR,
+ "Failed to send download cmd");
+
+ bg->fwl_reset_addr = false;
+ }
+
+ ret = cc1352_bootloader_send_data(bg, data + offset, size);
+ if (ret < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR,
+ "Failed to flash firmware");
+ *written = ret;
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err cc1352_poll_complete(struct fw_upload *fw_upload)
+{
+ u32 curr_crc32;
+ struct gb_beagleplay *bg = fw_upload->dd_handle;
+
+ if (cc1352_bootloader_crc32(bg, &curr_crc32) < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR,
+ "Failed to fetch crc32");
+
+ if (bg->fwl_crc32 != curr_crc32)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_FW_INVALID,
+ "Invalid CRC32");
+
+ if (cc1352_bootloader_reset(bg) < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR,
+ "Failed to reset");
+
+ dev_info(&bg->sd->dev, "CC1352 Flashing Successful");
+ WRITE_ONCE(bg->flashing_mode, false);
+ msleep(200);
+
+ if (gb_greybus_init(bg) < 0)
+ return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR,
+ "Failed to initialize greybus");
+
+ gb_beagleplay_start_svc(bg);
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void cc1352_cancel(struct fw_upload *fw_upload)
+{
+ struct gb_beagleplay *bg = fw_upload->dd_handle;
+
+ dev_info(&bg->sd->dev, "CC1352 Bootloader Cancel");
+
+ cc1352_bootloader_reset(bg);
+}
+
static void gb_serdev_deinit(struct gb_beagleplay *bg)
{
serdev_device_close(bg->sd);
@@ -463,6 +1031,65 @@ static int gb_serdev_init(struct gb_beagleplay *bg)
return 0;
}
+static const struct fw_upload_ops cc1352_bootloader_ops = {
+ .prepare = cc1352_prepare,
+ .write = cc1352_write,
+ .poll_complete = cc1352_poll_complete,
+ .cancel = cc1352_cancel,
+ .cleanup = cc1352_cleanup
+};
+
+static int gb_fw_init(struct gb_beagleplay *bg)
+{
+ int ret;
+ struct fw_upload *fwl;
+ struct gpio_desc *desc;
+
+ bg->fwl = NULL;
+ bg->bootloader_backdoor_gpio = NULL;
+ bg->rst_gpio = NULL;
+ bg->flashing_mode = false;
+ bg->fwl_cmd_response = 0;
+ bg->fwl_ack = 0;
+ init_completion(&bg->fwl_ack_com);
+ init_completion(&bg->fwl_cmd_response_com);
+
+ desc = devm_gpiod_get(&bg->sd->dev, "bootloader-backdoor", GPIOD_IN);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+ bg->bootloader_backdoor_gpio = desc;
+
+ desc = devm_gpiod_get(&bg->sd->dev, "reset", GPIOD_IN);
+ if (IS_ERR(desc)) {
+ ret = PTR_ERR(desc);
+ goto free_boot;
+ }
+ bg->rst_gpio = desc;
+
+ fwl = firmware_upload_register(THIS_MODULE, &bg->sd->dev, "cc1352p7",
+ &cc1352_bootloader_ops, bg);
+ if (IS_ERR(fwl)) {
+ ret = PTR_ERR(fwl);
+ goto free_reset;
+ }
+ bg->fwl = fwl;
+
+ return 0;
+
+free_reset:
+ devm_gpiod_put(&bg->sd->dev, bg->rst_gpio);
+ bg->rst_gpio = NULL;
+free_boot:
+ devm_gpiod_put(&bg->sd->dev, bg->bootloader_backdoor_gpio);
+ bg->bootloader_backdoor_gpio = NULL;
+ return ret;
+}
+
+static void gb_fw_deinit(struct gb_beagleplay *bg)
+{
+ firmware_upload_unregister(bg->fwl);
+}
+
static int gb_beagleplay_probe(struct serdev_device *serdev)
{
int ret = 0;
@@ -481,14 +1108,20 @@ static int gb_beagleplay_probe(struct serdev_device *serdev)
if (ret)
goto free_serdev;
- ret = gb_greybus_init(bg);
+ ret = gb_fw_init(bg);
if (ret)
goto free_hdlc;
+ ret = gb_greybus_init(bg);
+ if (ret)
+ goto free_fw;
+
gb_beagleplay_start_svc(bg);
return 0;
+free_fw:
+ gb_fw_deinit(bg);
free_hdlc:
hdlc_deinit(bg);
free_serdev:
@@ -500,6 +1133,7 @@ static void gb_beagleplay_remove(struct serdev_device *serdev)
{
struct gb_beagleplay *bg = serdev_device_get_drvdata(serdev);
+ gb_fw_deinit(bg);
gb_greybus_deinit(bg);
gb_beagleplay_stop_svc(bg);
hdlc_deinit(bg);
diff --git a/drivers/greybus/hd.c b/drivers/greybus/hd.c
index 72b21bf2d7d3..5de98d9177f1 100644
--- a/drivers/greybus/hd.c
+++ b/drivers/greybus/hd.c
@@ -50,7 +50,7 @@ int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id)
struct ida *id_map = &hd->cport_id_map;
int ret;
- ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL);
+ ret = ida_alloc_range(id_map, cport_id, cport_id, GFP_KERNEL);
if (ret < 0) {
dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id);
return ret;
@@ -64,7 +64,7 @@ void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id)
{
struct ida *id_map = &hd->cport_id_map;
- ida_simple_remove(id_map, cport_id);
+ ida_free(id_map, cport_id);
}
EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved);
@@ -80,16 +80,16 @@ int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id,
if (cport_id < 0) {
ida_start = 0;
- ida_end = hd->num_cports;
+ ida_end = hd->num_cports - 1;
} else if (cport_id < hd->num_cports) {
ida_start = cport_id;
- ida_end = cport_id + 1;
+ ida_end = cport_id;
} else {
dev_err(&hd->dev, "cport %d not available\n", cport_id);
return -EINVAL;
}
- return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
+ return ida_alloc_range(id_map, ida_start, ida_end, GFP_KERNEL);
}
/* Locking: Caller guarantees serialisation */
@@ -100,7 +100,7 @@ void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id)
return;
}
- ida_simple_remove(&hd->cport_id_map, cport_id);
+ ida_free(&hd->cport_id_map, cport_id);
}
static void gb_hd_release(struct device *dev)
@@ -111,12 +111,12 @@ static void gb_hd_release(struct device *dev)
if (hd->svc)
gb_svc_put(hd->svc);
- ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id);
+ ida_free(&gb_hd_bus_id_map, hd->bus_id);
ida_destroy(&hd->cport_id_map);
kfree(hd);
}
-struct device_type greybus_hd_type = {
+const struct device_type greybus_hd_type = {
.name = "greybus_host_device",
.release = gb_hd_release,
};
@@ -162,7 +162,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
if (!hd)
return ERR_PTR(-ENOMEM);
- ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL);
+ ret = ida_alloc_min(&gb_hd_bus_id_map, 1, GFP_KERNEL);
if (ret < 0) {
kfree(hd);
return ERR_PTR(ret);
diff --git a/drivers/greybus/interface.c b/drivers/greybus/interface.c
index 9ec949a438ef..a0f3e9422721 100644
--- a/drivers/greybus/interface.c
+++ b/drivers/greybus/interface.c
@@ -131,9 +131,8 @@ static int gb_interface_route_create(struct gb_interface *intf)
int ret;
/* Allocate an interface device id. */
- ret = ida_simple_get(&svc->device_id_map,
- GB_SVC_DEVICE_ID_MIN, GB_SVC_DEVICE_ID_MAX + 1,
- GFP_KERNEL);
+ ret = ida_alloc_range(&svc->device_id_map, GB_SVC_DEVICE_ID_MIN,
+ GB_SVC_DEVICE_ID_MAX, GFP_KERNEL);
if (ret < 0) {
dev_err(&intf->dev, "failed to allocate device id: %d\n", ret);
return ret;
@@ -165,7 +164,7 @@ err_svc_id_free:
* XXX anymore.
*/
err_ida_remove:
- ida_simple_remove(&svc->device_id_map, device_id);
+ ida_free(&svc->device_id_map, device_id);
return ret;
}
@@ -178,7 +177,7 @@ static void gb_interface_route_destroy(struct gb_interface *intf)
return;
gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id);
- ida_simple_remove(&svc->device_id_map, intf->device_id);
+ ida_free(&svc->device_id_map, intf->device_id);
intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
}
@@ -694,6 +693,7 @@ static void gb_interface_release(struct device *dev)
trace_gb_interface_release(intf);
+ cancel_work_sync(&intf->mode_switch_work);
kfree(intf);
}
@@ -765,7 +765,7 @@ static const struct dev_pm_ops gb_interface_pm_ops = {
gb_interface_runtime_idle)
};
-struct device_type greybus_interface_type = {
+const struct device_type greybus_interface_type = {
.name = "greybus_interface",
.release = gb_interface_release,
.pm = &gb_interface_pm_ops,
@@ -780,7 +780,7 @@ struct device_type greybus_interface_type = {
* The position of interface within the Endo is encoded in "interface_id"
* argument.
*
- * Returns a pointer to the new interfce or a null pointer if a
+ * Returns a pointer to the new interface or a null pointer if a
* failure occurs due to memory exhaustion.
*/
struct gb_interface *gb_interface_create(struct gb_module *module,
diff --git a/drivers/greybus/module.c b/drivers/greybus/module.c
index 36f77f9e1d74..7f7153a1dd60 100644
--- a/drivers/greybus/module.c
+++ b/drivers/greybus/module.c
@@ -81,7 +81,7 @@ static void gb_module_release(struct device *dev)
kfree(module);
}
-struct device_type greybus_module_type = {
+const struct device_type greybus_module_type = {
.name = "greybus_module",
.release = gb_module_release,
};
diff --git a/drivers/greybus/operation.c b/drivers/greybus/operation.c
index 8459e9bc0749..54ccc434a1f7 100644
--- a/drivers/greybus/operation.c
+++ b/drivers/greybus/operation.c
@@ -279,7 +279,7 @@ static void gb_operation_work(struct work_struct *work)
if (gb_operation_is_incoming(operation)) {
gb_operation_request_handle(operation);
} else {
- ret = del_timer_sync(&operation->timer);
+ ret = timer_delete_sync(&operation->timer);
if (!ret) {
/* Cancel request message if scheduled by timeout. */
if (gb_operation_result(operation) == -ETIMEDOUT)
@@ -295,7 +295,8 @@ static void gb_operation_work(struct work_struct *work)
static void gb_operation_timeout(struct timer_list *t)
{
- struct gb_operation *operation = from_timer(operation, t, timer);
+ struct gb_operation *operation = timer_container_of(operation, t,
+ timer);
if (gb_operation_result_set(operation, -ETIMEDOUT)) {
/*
diff --git a/drivers/greybus/svc.c b/drivers/greybus/svc.c
index 0d7e749174a4..4256467fcd35 100644
--- a/drivers/greybus/svc.c
+++ b/drivers/greybus/svc.c
@@ -1305,7 +1305,7 @@ static void gb_svc_release(struct device *dev)
kfree(svc);
}
-struct device_type greybus_svc_type = {
+const struct device_type greybus_svc_type = {
.name = "greybus_svc",
.release = gb_svc_release,
};