162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * motu-transaction.c - a part of driver for MOTU FireWire series 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "motu.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define SND_MOTU_ADDR_BASE 0xfffff0000000ULL 1262306a36Sopenharmony_ci#define ASYNC_ADDR_HI 0x0b04 1362306a36Sopenharmony_ci#define ASYNC_ADDR_LO 0x0b08 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciint snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg, 1662306a36Sopenharmony_ci size_t size) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci int tcode; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci if (size % sizeof(__be32) > 0 || size <= 0) 2162306a36Sopenharmony_ci return -EINVAL; 2262306a36Sopenharmony_ci if (size == sizeof(__be32)) 2362306a36Sopenharmony_ci tcode = TCODE_READ_QUADLET_REQUEST; 2462306a36Sopenharmony_ci else 2562306a36Sopenharmony_ci tcode = TCODE_READ_BLOCK_REQUEST; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci return snd_fw_transaction(motu->unit, tcode, 2862306a36Sopenharmony_ci SND_MOTU_ADDR_BASE + offset, reg, size, 0); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciint snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg, 3262306a36Sopenharmony_ci size_t size) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int tcode; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (size % sizeof(__be32) > 0 || size <= 0) 3762306a36Sopenharmony_ci return -EINVAL; 3862306a36Sopenharmony_ci if (size == sizeof(__be32)) 3962306a36Sopenharmony_ci tcode = TCODE_WRITE_QUADLET_REQUEST; 4062306a36Sopenharmony_ci else 4162306a36Sopenharmony_ci tcode = TCODE_WRITE_BLOCK_REQUEST; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return snd_fw_transaction(motu->unit, tcode, 4462306a36Sopenharmony_ci SND_MOTU_ADDR_BASE + offset, reg, size, 0); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void handle_message(struct fw_card *card, struct fw_request *request, 4862306a36Sopenharmony_ci int tcode, int destination, int source, 4962306a36Sopenharmony_ci int generation, unsigned long long offset, 5062306a36Sopenharmony_ci void *data, size_t length, void *callback_data) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct snd_motu *motu = callback_data; 5362306a36Sopenharmony_ci __be32 *buf = (__be32 *)data; 5462306a36Sopenharmony_ci unsigned long flags; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (tcode != TCODE_WRITE_QUADLET_REQUEST) { 5762306a36Sopenharmony_ci fw_send_response(card, request, RCODE_COMPLETE); 5862306a36Sopenharmony_ci return; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (offset != motu->async_handler.offset || length != 4) { 6262306a36Sopenharmony_ci fw_send_response(card, request, RCODE_ADDRESS_ERROR); 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci spin_lock_irqsave(&motu->lock, flags); 6762306a36Sopenharmony_ci motu->msg = be32_to_cpu(*buf); 6862306a36Sopenharmony_ci spin_unlock_irqrestore(&motu->lock, flags); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci fw_send_response(card, request, RCODE_COMPLETE); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci wake_up(&motu->hwdep_wait); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciint snd_motu_transaction_reregister(struct snd_motu *motu) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct fw_device *device = fw_parent_device(motu->unit); 7862306a36Sopenharmony_ci __be32 data; 7962306a36Sopenharmony_ci int err; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (motu->async_handler.callback_data == NULL) 8262306a36Sopenharmony_ci return -EINVAL; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Register messaging address. Block transaction is not allowed. */ 8562306a36Sopenharmony_ci data = cpu_to_be32((device->card->node_id << 16) | 8662306a36Sopenharmony_ci (motu->async_handler.offset >> 32)); 8762306a36Sopenharmony_ci err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, 8862306a36Sopenharmony_ci sizeof(data)); 8962306a36Sopenharmony_ci if (err < 0) 9062306a36Sopenharmony_ci return err; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci data = cpu_to_be32(motu->async_handler.offset); 9362306a36Sopenharmony_ci return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, 9462306a36Sopenharmony_ci sizeof(data)); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciint snd_motu_transaction_register(struct snd_motu *motu) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci static const struct fw_address_region resp_register_region = { 10062306a36Sopenharmony_ci .start = 0xffffe0000000ull, 10162306a36Sopenharmony_ci .end = 0xffffe000ffffull, 10262306a36Sopenharmony_ci }; 10362306a36Sopenharmony_ci int err; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Perhaps, 4 byte messages are transferred. */ 10662306a36Sopenharmony_ci motu->async_handler.length = 4; 10762306a36Sopenharmony_ci motu->async_handler.address_callback = handle_message; 10862306a36Sopenharmony_ci motu->async_handler.callback_data = motu; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci err = fw_core_add_address_handler(&motu->async_handler, 11162306a36Sopenharmony_ci &resp_register_region); 11262306a36Sopenharmony_ci if (err < 0) 11362306a36Sopenharmony_ci return err; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci err = snd_motu_transaction_reregister(motu); 11662306a36Sopenharmony_ci if (err < 0) { 11762306a36Sopenharmony_ci fw_core_remove_address_handler(&motu->async_handler); 11862306a36Sopenharmony_ci motu->async_handler.address_callback = NULL; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return err; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civoid snd_motu_transaction_unregister(struct snd_motu *motu) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci __be32 data; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (motu->async_handler.address_callback != NULL) 12962306a36Sopenharmony_ci fw_core_remove_address_handler(&motu->async_handler); 13062306a36Sopenharmony_ci motu->async_handler.address_callback = NULL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Unregister the address. */ 13362306a36Sopenharmony_ci data = cpu_to_be32(0x00000000); 13462306a36Sopenharmony_ci snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data)); 13562306a36Sopenharmony_ci snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data)); 13662306a36Sopenharmony_ci} 137