Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6:
  firewire: fw-ohci: shut up false compiler warning on PPC32
  firewire: fw-ohci: use dma_alloc_coherent for ar_buffer
  ieee1394: sbp2: fix for SYM13FW500 bridge (Datafab disk)
  firewire: fw-sbp2: fix for SYM13FW500 bridge (Datafab disk)
  firewire: update Kconfig help text
  firewire: warn on fatal condition in topology code
  firewire: fw-sbp2: set single-phase retry_limit
  firewire: fw-ohci: Apple UniNorth 1st generation support
  firewire: fw-ohci: PPC PMac platform code
  firewire: endianess annotations
  firewire: endianess fix
This commit is contained in:
Linus Torvalds 2008-03-14 16:49:41 -07:00
commit 4faa849665
7 changed files with 143 additions and 67 deletions

View File

@ -1,5 +1,3 @@
# -*- shell-script -*-
comment "An alternative FireWire stack is available with EXPERIMENTAL=y" comment "An alternative FireWire stack is available with EXPERIMENTAL=y"
depends on EXPERIMENTAL=n depends on EXPERIMENTAL=n
@ -21,27 +19,7 @@ config FIREWIRE
NOTE: NOTE:
You should only build ONE of the stacks, unless you REALLY know what You should only build ONE of the stacks, unless you REALLY know what
you are doing. If you install both, you should configure them only as you are doing.
modules rather than link them statically, and you should blacklist one
of the concurrent low-level drivers in /etc/modprobe.conf. Add either
blacklist firewire-ohci
or
blacklist ohci1394
there depending on which driver you DON'T want to have auto-loaded.
You can optionally do the same with the other IEEE 1394/ FireWire
drivers.
If you have an old modprobe which doesn't implement the blacklist
directive, use either
install firewire-ohci /bin/true
or
install ohci1394 /bin/true
and so on, depending on which modules you DON't want to have
auto-loaded.
config FIREWIRE_OHCI config FIREWIRE_OHCI
tristate "Support for OHCI FireWire host controllers" tristate "Support for OHCI FireWire host controllers"
@ -57,8 +35,24 @@ config FIREWIRE_OHCI
NOTE: NOTE:
If you also build ohci1394 of the classic stack, blacklist either You should only build ohci1394 or firewire-ohci, but not both.
ohci1394 or firewire-ohci to let hotplug load only the desired driver. If you nevertheless want to install both, you should configure them
only as modules and blacklist the driver(s) which you don't want to
have auto-loaded. Add either
blacklist firewire-ohci
or
blacklist ohci1394
blacklist video1394
blacklist dv1394
to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf
depending on your distribution. The latter two modules should be
blacklisted together with ohci1394 because they depend on ohci1394.
If you have an old modprobe which doesn't implement the blacklist
directive, use "install modulename /bin/true" for the modules to be
blacklisted.
config FIREWIRE_SBP2 config FIREWIRE_SBP2
tristate "Support for storage devices (SBP-2 protocol driver)" tristate "Support for storage devices (SBP-2 protocol driver)"
@ -75,9 +69,3 @@ config FIREWIRE_SBP2
You should also enable support for disks, CD-ROMs, etc. in the SCSI You should also enable support for disks, CD-ROMs, etc. in the SCSI
configuration section. configuration section.
NOTE:
If you also build sbp2 of the classic stack, blacklist either sbp2
or firewire-sbp2 to let hotplug load only the desired driver.

View File

@ -33,6 +33,10 @@
#include <asm/page.h> #include <asm/page.h>
#include <asm/system.h> #include <asm/system.h>
#ifdef CONFIG_PPC_PMAC
#include <asm/pmac_feature.h>
#endif
#include "fw-ohci.h" #include "fw-ohci.h"
#include "fw-transaction.h" #include "fw-transaction.h"
@ -175,6 +179,7 @@ struct fw_ohci {
int generation; int generation;
int request_generation; int request_generation;
u32 bus_seconds; u32 bus_seconds;
bool old_uninorth;
/* /*
* Spinlock for accessing fw_ohci data. Never call out of * Spinlock for accessing fw_ohci data. Never call out of
@ -276,19 +281,13 @@ static int ar_context_add_page(struct ar_context *ctx)
{ {
struct device *dev = ctx->ohci->card.device; struct device *dev = ctx->ohci->card.device;
struct ar_buffer *ab; struct ar_buffer *ab;
dma_addr_t ab_bus; dma_addr_t uninitialized_var(ab_bus);
size_t offset; size_t offset;
ab = (struct ar_buffer *) __get_free_page(GFP_ATOMIC); ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
if (ab == NULL) if (ab == NULL)
return -ENOMEM; return -ENOMEM;
ab_bus = dma_map_single(dev, ab, PAGE_SIZE, DMA_BIDIRECTIONAL);
if (dma_mapping_error(ab_bus)) {
free_page((unsigned long) ab);
return -ENOMEM;
}
memset(&ab->descriptor, 0, sizeof(ab->descriptor)); memset(&ab->descriptor, 0, sizeof(ab->descriptor));
ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE |
DESCRIPTOR_STATUS | DESCRIPTOR_STATUS |
@ -299,8 +298,6 @@ static int ar_context_add_page(struct ar_context *ctx)
ab->descriptor.res_count = cpu_to_le16(PAGE_SIZE - offset); ab->descriptor.res_count = cpu_to_le16(PAGE_SIZE - offset);
ab->descriptor.branch_address = 0; ab->descriptor.branch_address = 0;
dma_sync_single_for_device(dev, ab_bus, PAGE_SIZE, DMA_BIDIRECTIONAL);
ctx->last_buffer->descriptor.branch_address = cpu_to_le32(ab_bus | 1); ctx->last_buffer->descriptor.branch_address = cpu_to_le32(ab_bus | 1);
ctx->last_buffer->next = ab; ctx->last_buffer->next = ab;
ctx->last_buffer = ab; ctx->last_buffer = ab;
@ -311,15 +308,22 @@ static int ar_context_add_page(struct ar_context *ctx)
return 0; return 0;
} }
#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
#define cond_le32_to_cpu(v) \
(ohci->old_uninorth ? (__force __u32)(v) : le32_to_cpu(v))
#else
#define cond_le32_to_cpu(v) le32_to_cpu(v)
#endif
static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
{ {
struct fw_ohci *ohci = ctx->ohci; struct fw_ohci *ohci = ctx->ohci;
struct fw_packet p; struct fw_packet p;
u32 status, length, tcode; u32 status, length, tcode;
p.header[0] = le32_to_cpu(buffer[0]); p.header[0] = cond_le32_to_cpu(buffer[0]);
p.header[1] = le32_to_cpu(buffer[1]); p.header[1] = cond_le32_to_cpu(buffer[1]);
p.header[2] = le32_to_cpu(buffer[2]); p.header[2] = cond_le32_to_cpu(buffer[2]);
tcode = (p.header[0] >> 4) & 0x0f; tcode = (p.header[0] >> 4) & 0x0f;
switch (tcode) { switch (tcode) {
@ -331,7 +335,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
break; break;
case TCODE_READ_BLOCK_REQUEST : case TCODE_READ_BLOCK_REQUEST :
p.header[3] = le32_to_cpu(buffer[3]); p.header[3] = cond_le32_to_cpu(buffer[3]);
p.header_length = 16; p.header_length = 16;
p.payload_length = 0; p.payload_length = 0;
break; break;
@ -340,7 +344,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
case TCODE_READ_BLOCK_RESPONSE: case TCODE_READ_BLOCK_RESPONSE:
case TCODE_LOCK_REQUEST: case TCODE_LOCK_REQUEST:
case TCODE_LOCK_RESPONSE: case TCODE_LOCK_RESPONSE:
p.header[3] = le32_to_cpu(buffer[3]); p.header[3] = cond_le32_to_cpu(buffer[3]);
p.header_length = 16; p.header_length = 16;
p.payload_length = p.header[3] >> 16; p.payload_length = p.header[3] >> 16;
break; break;
@ -357,7 +361,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
/* FIXME: What to do about evt_* errors? */ /* FIXME: What to do about evt_* errors? */
length = (p.header_length + p.payload_length + 3) / 4; length = (p.header_length + p.payload_length + 3) / 4;
status = le32_to_cpu(buffer[length]); status = cond_le32_to_cpu(buffer[length]);
p.ack = ((status >> 16) & 0x1f) - 16; p.ack = ((status >> 16) & 0x1f) - 16;
p.speed = (status >> 21) & 0x7; p.speed = (status >> 21) & 0x7;
@ -375,7 +379,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
*/ */
if (p.ack + 16 == 0x09) if (p.ack + 16 == 0x09)
ohci->request_generation = (buffer[2] >> 16) & 0xff; ohci->request_generation = (p.header[2] >> 16) & 0xff;
else if (ctx == &ohci->ar_request_ctx) else if (ctx == &ohci->ar_request_ctx)
fw_core_handle_request(&ohci->card, &p); fw_core_handle_request(&ohci->card, &p);
else else
@ -397,6 +401,7 @@ static void ar_context_tasklet(unsigned long data)
if (d->res_count == 0) { if (d->res_count == 0) {
size_t size, rest, offset; size_t size, rest, offset;
dma_addr_t buffer_bus;
/* /*
* This descriptor is finished and we may have a * This descriptor is finished and we may have a
@ -405,9 +410,7 @@ static void ar_context_tasklet(unsigned long data)
*/ */
offset = offsetof(struct ar_buffer, data); offset = offsetof(struct ar_buffer, data);
dma_unmap_single(ohci->card.device, buffer_bus = le32_to_cpu(ab->descriptor.data_address) - offset;
le32_to_cpu(ab->descriptor.data_address) - offset,
PAGE_SIZE, DMA_BIDIRECTIONAL);
buffer = ab; buffer = ab;
ab = ab->next; ab = ab->next;
@ -423,7 +426,8 @@ static void ar_context_tasklet(unsigned long data)
while (buffer < end) while (buffer < end)
buffer = handle_ar_packet(ctx, buffer); buffer = handle_ar_packet(ctx, buffer);
free_page((unsigned long)buffer); dma_free_coherent(ohci->card.device, PAGE_SIZE,
buffer, buffer_bus);
ar_context_add_page(ctx); ar_context_add_page(ctx);
} else { } else {
buffer = ctx->pointer; buffer = ctx->pointer;
@ -532,7 +536,7 @@ static int
context_add_buffer(struct context *ctx) context_add_buffer(struct context *ctx)
{ {
struct descriptor_buffer *desc; struct descriptor_buffer *desc;
dma_addr_t bus_addr; dma_addr_t uninitialized_var(bus_addr);
int offset; int offset;
/* /*
@ -1022,13 +1026,14 @@ static void bus_reset_tasklet(unsigned long data)
*/ */
self_id_count = (reg_read(ohci, OHCI1394_SelfIDCount) >> 3) & 0x3ff; self_id_count = (reg_read(ohci, OHCI1394_SelfIDCount) >> 3) & 0x3ff;
generation = (le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff; generation = (cond_le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff;
rmb(); rmb();
for (i = 1, j = 0; j < self_id_count; i += 2, j++) { for (i = 1, j = 0; j < self_id_count; i += 2, j++) {
if (ohci->self_id_cpu[i] != ~ohci->self_id_cpu[i + 1]) if (ohci->self_id_cpu[i] != ~ohci->self_id_cpu[i + 1])
fw_error("inconsistent self IDs\n"); fw_error("inconsistent self IDs\n");
ohci->self_id_buffer[j] = le32_to_cpu(ohci->self_id_cpu[i]); ohci->self_id_buffer[j] =
cond_le32_to_cpu(ohci->self_id_cpu[i]);
} }
rmb(); rmb();
@ -1316,7 +1321,7 @@ ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length)
unsigned long flags; unsigned long flags;
int retval = -EBUSY; int retval = -EBUSY;
__be32 *next_config_rom; __be32 *next_config_rom;
dma_addr_t next_config_rom_bus; dma_addr_t uninitialized_var(next_config_rom_bus);
ohci = fw_ohci(card); ohci = fw_ohci(card);
@ -1487,7 +1492,7 @@ static int handle_ir_dualbuffer_packet(struct context *context,
void *p, *end; void *p, *end;
int i; int i;
if (db->first_res_count > 0 && db->second_res_count > 0) { if (db->first_res_count != 0 && db->second_res_count != 0) {
if (ctx->excess_bytes <= le16_to_cpu(db->second_req_count)) { if (ctx->excess_bytes <= le16_to_cpu(db->second_req_count)) {
/* This descriptor isn't done yet, stop iteration. */ /* This descriptor isn't done yet, stop iteration. */
return 0; return 0;
@ -1513,7 +1518,7 @@ static int handle_ir_dualbuffer_packet(struct context *context,
memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4); memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
i += ctx->base.header_size; i += ctx->base.header_size;
ctx->excess_bytes += ctx->excess_bytes +=
(le32_to_cpu(*(u32 *)(p + 4)) >> 16) & 0xffff; (le32_to_cpu(*(__le32 *)(p + 4)) >> 16) & 0xffff;
p += ctx->base.header_size + 4; p += ctx->base.header_size + 4;
} }
ctx->header_length = i; ctx->header_length = i;
@ -2048,6 +2053,18 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
int err; int err;
size_t size; size_t size;
#ifdef CONFIG_PPC_PMAC
/* Necessary on some machines if fw-ohci was loaded/ unloaded before */
if (machine_is(powermac)) {
struct device_node *ofn = pci_device_to_OF_node(dev);
if (ofn) {
pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 1);
pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1);
}
}
#endif /* CONFIG_PPC_PMAC */
ohci = kzalloc(sizeof(*ohci), GFP_KERNEL); ohci = kzalloc(sizeof(*ohci), GFP_KERNEL);
if (ohci == NULL) { if (ohci == NULL) {
fw_error("Could not malloc fw_ohci data.\n"); fw_error("Could not malloc fw_ohci data.\n");
@ -2066,6 +2083,10 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
pci_set_drvdata(dev, ohci); pci_set_drvdata(dev, ohci);
#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE &&
dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW;
#endif
spin_lock_init(&ohci->lock); spin_lock_init(&ohci->lock);
tasklet_init(&ohci->bus_reset_tasklet, tasklet_init(&ohci->bus_reset_tasklet,
@ -2182,6 +2203,19 @@ static void pci_remove(struct pci_dev *dev)
pci_disable_device(dev); pci_disable_device(dev);
fw_card_put(&ohci->card); fw_card_put(&ohci->card);
#ifdef CONFIG_PPC_PMAC
/* On UniNorth, power down the cable and turn off the chip clock
* to save power on laptops */
if (machine_is(powermac)) {
struct device_node *ofn = pci_device_to_OF_node(dev);
if (ofn) {
pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0);
pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 0);
}
}
#endif /* CONFIG_PPC_PMAC */
fw_notify("Removed fw-ohci device.\n"); fw_notify("Removed fw-ohci device.\n");
} }
@ -2202,6 +2236,16 @@ static int pci_suspend(struct pci_dev *pdev, pm_message_t state)
if (err) if (err)
fw_error("pci_set_power_state failed with %d\n", err); fw_error("pci_set_power_state failed with %d\n", err);
/* PowerMac suspend code comes last */
#ifdef CONFIG_PPC_PMAC
if (machine_is(powermac)) {
struct device_node *ofn = pci_device_to_OF_node(pdev);
if (ofn)
pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0);
}
#endif /* CONFIG_PPC_PMAC */
return 0; return 0;
} }
@ -2210,6 +2254,16 @@ static int pci_resume(struct pci_dev *pdev)
struct fw_ohci *ohci = pci_get_drvdata(pdev); struct fw_ohci *ohci = pci_get_drvdata(pdev);
int err; int err;
/* PowerMac resume code comes first */
#ifdef CONFIG_PPC_PMAC
if (machine_is(powermac)) {
struct device_node *ofn = pci_device_to_OF_node(pdev);
if (ofn)
pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1);
}
#endif /* CONFIG_PPC_PMAC */
pci_set_power_state(pdev, PCI_D0); pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev); pci_restore_state(pdev);
err = pci_enable_device(pdev); err = pci_enable_device(pdev);

View File

@ -173,6 +173,7 @@ struct sbp2_target {
#define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */ #define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */
#define SBP2_ORB_NULL 0x80000000 #define SBP2_ORB_NULL 0x80000000
#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000 #define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000
#define SBP2_RETRY_LIMIT 0xf /* 15 retries */
#define SBP2_DIRECTION_TO_MEDIA 0x0 #define SBP2_DIRECTION_TO_MEDIA 0x0
#define SBP2_DIRECTION_FROM_MEDIA 0x1 #define SBP2_DIRECTION_FROM_MEDIA 0x1
@ -330,6 +331,11 @@ static const struct {
.model = ~0, .model = ~0,
.workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
}, },
/* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ {
.firmware_revision = 0x002600,
.model = ~0,
.workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
},
/* /*
* There are iPods (2nd gen, 3rd gen) with model_id == 0, but * There are iPods (2nd gen, 3rd gen) with model_id == 0, but
@ -812,6 +818,30 @@ static void sbp2_target_put(struct sbp2_target *tgt)
kref_put(&tgt->kref, sbp2_release_target); kref_put(&tgt->kref, sbp2_release_target);
} }
static void
complete_set_busy_timeout(struct fw_card *card, int rcode,
void *payload, size_t length, void *done)
{
complete(done);
}
static void sbp2_set_busy_timeout(struct sbp2_logical_unit *lu)
{
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
DECLARE_COMPLETION_ONSTACK(done);
struct fw_transaction t;
static __be32 busy_timeout;
/* FIXME: we should try to set dual-phase cycle_limit too */
busy_timeout = cpu_to_be32(SBP2_RETRY_LIMIT);
fw_send_request(device->card, &t, TCODE_WRITE_QUADLET_REQUEST,
lu->tgt->node_id, lu->generation, device->max_speed,
CSR_REGISTER_BASE + CSR_BUSY_TIMEOUT, &busy_timeout,
sizeof(busy_timeout), complete_set_busy_timeout, &done);
wait_for_completion(&done);
}
static void sbp2_reconnect(struct work_struct *work); static void sbp2_reconnect(struct work_struct *work);
static void sbp2_login(struct work_struct *work) static void sbp2_login(struct work_struct *work)
@ -864,10 +894,8 @@ static void sbp2_login(struct work_struct *work)
fw_notify("%s: logged in to LUN %04x (%d retries)\n", fw_notify("%s: logged in to LUN %04x (%d retries)\n",
tgt->bus_id, lu->lun, lu->retries); tgt->bus_id, lu->lun, lu->retries);
#if 0 /* set appropriate retry limit(s) in BUSY_TIMEOUT register */
/* FIXME: The linux1394 sbp2 does this last step. */ sbp2_set_busy_timeout(lu);
sbp2_set_busy_timeout(scsi_id);
#endif
PREPARE_DELAYED_WORK(&lu->work, sbp2_reconnect); PREPARE_DELAYED_WORK(&lu->work, sbp2_reconnect);
sbp2_agent_reset(lu); sbp2_agent_reset(lu);

View File

@ -21,6 +21,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <asm/bug.h>
#include <asm/system.h> #include <asm/system.h>
#include "fw-transaction.h" #include "fw-transaction.h"
#include "fw-topology.h" #include "fw-topology.h"
@ -424,8 +425,8 @@ update_tree(struct fw_card *card, struct fw_node *root)
node1 = fw_node(list1.next); node1 = fw_node(list1.next);
while (&node0->link != &list0) { while (&node0->link != &list0) {
WARN_ON(node0->port_count != node1->port_count);
/* assert(node0->port_count == node1->port_count); */
if (node0->link_on && !node1->link_on) if (node0->link_on && !node1->link_on)
event = FW_NODE_LINK_OFF; event = FW_NODE_LINK_OFF;
else if (!node0->link_on && node1->link_on) else if (!node0->link_on && node1->link_on)

View File

@ -751,7 +751,7 @@ handle_topology_map(struct fw_card *card, struct fw_request *request,
void *payload, size_t length, void *callback_data) void *payload, size_t length, void *callback_data)
{ {
int i, start, end; int i, start, end;
u32 *map; __be32 *map;
if (!TCODE_IS_READ_REQUEST(tcode)) { if (!TCODE_IS_READ_REQUEST(tcode)) {
fw_send_response(card, request, RCODE_TYPE_ERROR); fw_send_response(card, request, RCODE_TYPE_ERROR);

View File

@ -86,12 +86,12 @@
static inline void static inline void
fw_memcpy_from_be32(void *_dst, void *_src, size_t size) fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
{ {
u32 *dst = _dst; u32 *dst = _dst;
u32 *src = _src; __be32 *src = _src;
int i; int i;
for (i = 0; i < size / 4; i++) for (i = 0; i < size / 4; i++)
dst[i] = cpu_to_be32(src[i]); dst[i] = be32_to_cpu(src[i]);
} }
static inline void static inline void

View File

@ -376,6 +376,11 @@ static const struct {
.model_id = SBP2_ROM_VALUE_WILDCARD, .model_id = SBP2_ROM_VALUE_WILDCARD,
.workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
}, },
/* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ {
.firmware_revision = 0x002600,
.model_id = SBP2_ROM_VALUE_WILDCARD,
.workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
},
/* iPod 4th generation */ { /* iPod 4th generation */ {
.firmware_revision = 0x0a2700, .firmware_revision = 0x0a2700,
.model_id = 0x000021, .model_id = 0x000021,