Commit 3ee40c37 authored by David S. Miller's avatar David S. Miller
parents 67002547 98eb0f53
...@@ -505,8 +505,15 @@ void i2400m_report_hook(struct i2400m *i2400m, ...@@ -505,8 +505,15 @@ void i2400m_report_hook(struct i2400m *i2400m,
* it. */ * it. */
case I2400M_MT_REPORT_POWERSAVE_READY: /* zzzzz */ case I2400M_MT_REPORT_POWERSAVE_READY: /* zzzzz */
if (l3l4_hdr->status == cpu_to_le16(I2400M_MS_DONE_OK)) { if (l3l4_hdr->status == cpu_to_le16(I2400M_MS_DONE_OK)) {
d_printf(1, dev, "ready for powersave, requesting\n"); if (i2400m_power_save_disabled)
i2400m_cmd_enter_powersave(i2400m); d_printf(1, dev, "ready for powersave, "
"not requesting (disabled by module "
"parameter)\n");
else {
d_printf(1, dev, "ready for powersave, "
"requesting\n");
i2400m_cmd_enter_powersave(i2400m);
}
} }
break; break;
}; };
...@@ -688,8 +695,9 @@ struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m, ...@@ -688,8 +695,9 @@ struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m,
d_fnstart(3, dev, "(i2400m %p buf %p len %zu)\n", d_fnstart(3, dev, "(i2400m %p buf %p len %zu)\n",
i2400m, buf, buf_len); i2400m, buf, buf_len);
rmb(); /* Make sure we see what i2400m_dev_reset_handle() */
if (i2400m->boot_mode) if (i2400m->boot_mode)
return ERR_PTR(-ENODEV); return ERR_PTR(-EL3RST);
msg_l3l4_hdr = buf; msg_l3l4_hdr = buf;
/* Check msg & payload consistency */ /* Check msg & payload consistency */
...@@ -1389,16 +1397,16 @@ int i2400m_dev_initialize(struct i2400m *i2400m) ...@@ -1389,16 +1397,16 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
* *
* @i2400m: device descriptor * @i2400m: device descriptor
* *
* Gracefully stops the device, moving it to the lowest power * Release resources acquired during the running of the device; in
* consumption state possible. * theory, should also tell the device to go to sleep, switch off the
* radio, all that, but at this point, in most cases (driver
* disconnection, reset handling) we can't even talk to the device.
*/ */
void i2400m_dev_shutdown(struct i2400m *i2400m) void i2400m_dev_shutdown(struct i2400m *i2400m)
{ {
int result = -ENODEV;
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(i2400m %p)\n", i2400m); d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
result = i2400m->bus_reset(i2400m, I2400M_RT_WARM); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
d_fnend(3, dev, "(i2400m %p) = void [%d]\n", i2400m, result);
return; return;
} }
...@@ -82,6 +82,14 @@ module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644); ...@@ -82,6 +82,14 @@ module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644);
MODULE_PARM_DESC(rx_reorder_disabled, MODULE_PARM_DESC(rx_reorder_disabled,
"If true, RX reordering will be disabled."); "If true, RX reordering will be disabled.");
int i2400m_power_save_disabled; /* 0 (power saving enabled) by default */
module_param_named(power_save_disabled, i2400m_power_save_disabled, int, 0644);
MODULE_PARM_DESC(power_save_disabled,
"If true, the driver will not tell the device to enter "
"power saving mode when it reports it is ready for it. "
"False by default (so the device is told to do power "
"saving).");
/** /**
* i2400m_queue_work - schedule work on a i2400m's queue * i2400m_queue_work - schedule work on a i2400m's queue
* *
...@@ -172,7 +180,6 @@ int i2400m_schedule_work(struct i2400m *i2400m, ...@@ -172,7 +180,6 @@ int i2400m_schedule_work(struct i2400m *i2400m,
int result; int result;
struct i2400m_work *iw; struct i2400m_work *iw;
BUG_ON(i2400m->work_queue == NULL);
result = -ENOMEM; result = -ENOMEM;
iw = kzalloc(sizeof(*iw), gfp_flags); iw = kzalloc(sizeof(*iw), gfp_flags);
if (iw == NULL) if (iw == NULL)
...@@ -377,6 +384,11 @@ int i2400m_check_mac_addr(struct i2400m *i2400m) ...@@ -377,6 +384,11 @@ int i2400m_check_mac_addr(struct i2400m *i2400m)
* Uploads firmware and brings up all the resources needed to be able * Uploads firmware and brings up all the resources needed to be able
* to communicate with the device. * to communicate with the device.
* *
* The workqueue has to be setup early, at least before RX handling
* (it's only real user for now) so it can process reports as they
* arrive. We also want to destroy it if we retry, to make sure it is
* flushed...easier like this.
*
* TX needs to be setup before the bus-specific code (otherwise on * TX needs to be setup before the bus-specific code (otherwise on
* shutdown, the bus-tx code could try to access it). * shutdown, the bus-tx code could try to access it).
*/ */
...@@ -387,7 +399,7 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags) ...@@ -387,7 +399,7 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags)
struct wimax_dev *wimax_dev = &i2400m->wimax_dev; struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
struct net_device *net_dev = wimax_dev->net_dev; struct net_device *net_dev = wimax_dev->net_dev;
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
int times = 3; int times = i2400m->bus_bm_retries;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m); d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
retry: retry:
...@@ -402,15 +414,15 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags) ...@@ -402,15 +414,15 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags)
result = i2400m_rx_setup(i2400m); result = i2400m_rx_setup(i2400m);
if (result < 0) if (result < 0)
goto error_rx_setup; goto error_rx_setup;
result = i2400m->bus_dev_start(i2400m);
if (result < 0)
goto error_bus_dev_start;
i2400m->work_queue = create_singlethread_workqueue(wimax_dev->name); i2400m->work_queue = create_singlethread_workqueue(wimax_dev->name);
if (i2400m->work_queue == NULL) { if (i2400m->work_queue == NULL) {
result = -ENOMEM; result = -ENOMEM;
dev_err(dev, "cannot create workqueue\n"); dev_err(dev, "cannot create workqueue\n");
goto error_create_workqueue; goto error_create_workqueue;
} }
result = i2400m->bus_dev_start(i2400m);
if (result < 0)
goto error_bus_dev_start;
result = i2400m_firmware_check(i2400m); /* fw versions ok? */ result = i2400m_firmware_check(i2400m); /* fw versions ok? */
if (result < 0) if (result < 0)
goto error_fw_check; goto error_fw_check;
...@@ -432,17 +444,17 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags) ...@@ -432,17 +444,17 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags)
error_dev_initialize: error_dev_initialize:
error_check_mac_addr: error_check_mac_addr:
error_fw_check: error_fw_check:
destroy_workqueue(i2400m->work_queue);
error_create_workqueue:
i2400m->bus_dev_stop(i2400m); i2400m->bus_dev_stop(i2400m);
error_bus_dev_start: error_bus_dev_start:
destroy_workqueue(i2400m->work_queue);
error_create_workqueue:
i2400m_rx_release(i2400m); i2400m_rx_release(i2400m);
error_rx_setup: error_rx_setup:
i2400m_tx_release(i2400m); i2400m_tx_release(i2400m);
error_tx_setup: error_tx_setup:
error_bootstrap: error_bootstrap:
if (result == -ERESTARTSYS && times-- > 0) { if (result == -EL3RST && times-- > 0) {
flags = I2400M_BRI_SOFT; flags = I2400M_BRI_SOFT|I2400M_BRI_MAC_REINIT;
goto retry; goto retry;
} }
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
...@@ -471,7 +483,9 @@ int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) ...@@ -471,7 +483,9 @@ int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags)
* *
* Returns: 0 if ok, < 0 errno code on error. * Returns: 0 if ok, < 0 errno code on error.
* *
* Releases all the resources allocated to communicate with the device. * Releases all the resources allocated to communicate with the
* device. Note we cannot destroy the workqueue earlier as until RX is
* fully destroyed, it could still try to schedule jobs.
*/ */
static static
void __i2400m_dev_stop(struct i2400m *i2400m) void __i2400m_dev_stop(struct i2400m *i2400m)
...@@ -483,8 +497,8 @@ void __i2400m_dev_stop(struct i2400m *i2400m) ...@@ -483,8 +497,8 @@ void __i2400m_dev_stop(struct i2400m *i2400m)
wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING);
i2400m_dev_shutdown(i2400m); i2400m_dev_shutdown(i2400m);
i2400m->ready = 0; i2400m->ready = 0;
destroy_workqueue(i2400m->work_queue);
i2400m->bus_dev_stop(i2400m); i2400m->bus_dev_stop(i2400m);
destroy_workqueue(i2400m->work_queue);
i2400m_rx_release(i2400m); i2400m_rx_release(i2400m);
i2400m_tx_release(i2400m); i2400m_tx_release(i2400m);
wimax_state_change(wimax_dev, WIMAX_ST_DOWN); wimax_state_change(wimax_dev, WIMAX_ST_DOWN);
...@@ -546,7 +560,7 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) ...@@ -546,7 +560,7 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)
* i2400m_dev_stop() [we are shutting down anyway, so * i2400m_dev_stop() [we are shutting down anyway, so
* ignore it] or we are resetting somewhere else. */ * ignore it] or we are resetting somewhere else. */
dev_err(dev, "device rebooted\n"); dev_err(dev, "device rebooted\n");
i2400m_msg_to_dev_cancel_wait(i2400m, -ERESTARTSYS); i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST);
complete(&i2400m->msg_completion); complete(&i2400m->msg_completion);
goto out; goto out;
} }
...@@ -596,6 +610,8 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) ...@@ -596,6 +610,8 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)
*/ */
int i2400m_dev_reset_handle(struct i2400m *i2400m) int i2400m_dev_reset_handle(struct i2400m *i2400m)
{ {
i2400m->boot_mode = 1;
wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */
return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle,
GFP_ATOMIC); GFP_ATOMIC);
} }
......
...@@ -397,7 +397,7 @@ static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk, ...@@ -397,7 +397,7 @@ static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk,
unsigned int direct, unsigned int do_csum) unsigned int direct, unsigned int do_csum)
{ {
int ret; int ret;
size_t chunk_len = ALIGN(__chunk_len, I2400M_PL_PAD); size_t chunk_len = ALIGN(__chunk_len, I2400M_PL_ALIGN);
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
struct { struct {
struct i2400m_bootrom_header cmd; struct i2400m_bootrom_header cmd;
...@@ -532,14 +532,14 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, ...@@ -532,14 +532,14 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
cmd = (void *) bcf + offset; cmd = (void *) bcf + offset;
if (i2400m->sboot == 0) { if (i2400m->sboot == 0) {
struct i2400m_bootrom_header jump_ack; struct i2400m_bootrom_header jump_ack;
d_printf(3, dev, "unsecure boot, jumping to 0x%08x\n", d_printf(1, dev, "unsecure boot, jumping to 0x%08x\n",
le32_to_cpu(cmd->target_addr)); le32_to_cpu(cmd->target_addr));
i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP); i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP);
cmd->data_size = 0; cmd->data_size = 0;
ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
&jump_ack, sizeof(jump_ack), 0); &jump_ack, sizeof(jump_ack), 0);
} else { } else {
d_printf(3, dev, "secure boot, jumping to 0x%08x\n", d_printf(1, dev, "secure boot, jumping to 0x%08x\n",
le32_to_cpu(cmd->target_addr)); le32_to_cpu(cmd->target_addr));
cmd_buf = i2400m->bm_cmd_buf; cmd_buf = i2400m->bm_cmd_buf;
memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd));
...@@ -696,8 +696,7 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) ...@@ -696,8 +696,7 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)
return result; return result;
error_timeout: error_timeout:
dev_err(dev, "Timed out waiting for reboot ack, resetting\n"); dev_err(dev, "Timed out waiting for reboot ack\n");
i2400m->bus_reset(i2400m, I2400M_RT_BUS);
result = -ETIMEDOUT; result = -ETIMEDOUT;
goto exit_timeout; goto exit_timeout;
} }
...@@ -770,40 +769,21 @@ int i2400m_read_mac_addr(struct i2400m *i2400m) ...@@ -770,40 +769,21 @@ int i2400m_read_mac_addr(struct i2400m *i2400m)
static static
int i2400m_dnload_init_nonsigned(struct i2400m *i2400m) int i2400m_dnload_init_nonsigned(struct i2400m *i2400m)
{ {
#define POKE(a, d) { \ unsigned i = 0;
.address = cpu_to_le32(a), \ int ret = 0;
.data = cpu_to_le32(d) \
}
static const struct {
__le32 address;
__le32 data;
} i2400m_pokes[] = {
POKE(0x081A58, 0xA7810230),
POKE(0x080040, 0x00000000),
POKE(0x080048, 0x00000082),
POKE(0x08004C, 0x0000081F),
POKE(0x080054, 0x00000085),
POKE(0x080058, 0x00000180),
POKE(0x08005C, 0x00000018),
POKE(0x080060, 0x00000010),
POKE(0x080574, 0x00000001),
POKE(0x080550, 0x00000005),
POKE(0xAE0000, 0x00000000),
};
#undef POKE
unsigned i;
int ret;
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
dev_warn(dev, "WARNING!!! non-signed boot UNTESTED PATH!\n");
d_fnstart(5, dev, "(i2400m %p)\n", i2400m); d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
for (i = 0; i < ARRAY_SIZE(i2400m_pokes); i++) { if (i2400m->bus_bm_pokes_table) {
ret = i2400m_download_chunk(i2400m, &i2400m_pokes[i].data, while (i2400m->bus_bm_pokes_table[i].address) {
sizeof(i2400m_pokes[i].data), ret = i2400m_download_chunk(
i2400m_pokes[i].address, 1, 1); i2400m,
if (ret < 0) &i2400m->bus_bm_pokes_table[i].data,
break; sizeof(i2400m->bus_bm_pokes_table[i].data),
i2400m->bus_bm_pokes_table[i].address, 1, 1);
if (ret < 0)
break;
i++;
}
} }
d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
return ret; return ret;
...@@ -980,11 +960,12 @@ int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf, ...@@ -980,11 +960,12 @@ int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf,
{ {
int ret = 0; int ret = 0;
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
int count = I2400M_BOOT_RETRIES; int count = i2400m->bus_bm_retries;
d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n", d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n",
i2400m, bcf, bcf_size); i2400m, bcf, bcf_size);
i2400m->boot_mode = 1; i2400m->boot_mode = 1;
wmb(); /* Make sure other readers see it */
hw_reboot: hw_reboot:
if (count-- == 0) { if (count-- == 0) {
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
...@@ -1033,6 +1014,7 @@ int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf, ...@@ -1033,6 +1014,7 @@ int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf,
d_printf(2, dev, "fw %s successfully uploaded\n", d_printf(2, dev, "fw %s successfully uploaded\n",
i2400m->fw_name); i2400m->fw_name);
i2400m->boot_mode = 0; i2400m->boot_mode = 0;
wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */
error_dnload_finalize: error_dnload_finalize:
error_dnload_bcf: error_dnload_bcf:
error_dnload_init: error_dnload_init:
......
...@@ -78,6 +78,8 @@ enum { ...@@ -78,6 +78,8 @@ enum {
/* The number of ticks to wait for the device to signal that /* The number of ticks to wait for the device to signal that
* it is ready */ * it is ready */
I2400MS_INIT_SLEEP_INTERVAL = 10, I2400MS_INIT_SLEEP_INTERVAL = 10,
/* How long to wait for the device to settle after reset */
I2400MS_SETTLE_TIME = 40,
}; };
...@@ -105,6 +107,10 @@ struct i2400ms { ...@@ -105,6 +107,10 @@ struct i2400ms {
char tx_wq_name[32]; char tx_wq_name[32];
struct dentry *debugfs_dentry; struct dentry *debugfs_dentry;
wait_queue_head_t bm_wfa_wq;
int bm_wait_result;
size_t bm_ack_size;
}; };
...@@ -129,4 +135,7 @@ extern ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *, ...@@ -129,4 +135,7 @@ extern ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *,
extern ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *, extern ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *,
struct i2400m_bootrom_header *, struct i2400m_bootrom_header *,
size_t); size_t);
extern void i2400ms_bus_bm_release(struct i2400m *);
extern int i2400ms_bus_bm_setup(struct i2400m *);
#endif /* #ifndef __I2400M_SDIO_H__ */ #endif /* #ifndef __I2400M_SDIO_H__ */
...@@ -150,11 +150,33 @@ ...@@ -150,11 +150,33 @@
enum { enum {
/* Firmware uploading */ /* Firmware uploading */
I2400M_BOOT_RETRIES = 3, I2400M_BOOT_RETRIES = 3,
I3200_BOOT_RETRIES = 3,
/* Size of the Boot Mode Command buffer */ /* Size of the Boot Mode Command buffer */
I2400M_BM_CMD_BUF_SIZE = 16 * 1024, I2400M_BM_CMD_BUF_SIZE = 16 * 1024,
I2400M_BM_ACK_BUF_SIZE = 256, I2400M_BM_ACK_BUF_SIZE = 256,
}; };
/**
* struct i2400m_poke_table - Hardware poke table for the Intel 2400m
*
* This structure will be used to create a device specific poke table
* to put the device in a consistant state at boot time.
*
* @address: The device address to poke
*
* @data: The data value to poke to the device address
*
*/
struct i2400m_poke_table{
__le32 address;
__le32 data;
};
#define I2400M_FW_POKE(a, d) { \
.address = cpu_to_le32(a), \
.data = cpu_to_le32(d) \
}
/** /**
* i2400m_reset_type - methods to reset a device * i2400m_reset_type - methods to reset a device
...@@ -224,6 +246,17 @@ struct i2400m_roq; ...@@ -224,6 +246,17 @@ struct i2400m_roq;
* process, so it cannot rely on common infrastructure being laid * process, so it cannot rely on common infrastructure being laid
* out. * out.
* *
* @bus_bm_retries: [fill] How many times shall a firmware upload /
* device initialization be retried? Different models of the same
* device might need different values, hence it is set by the
* bus-specific driver. Note this value is used in two places,
* i2400m_fw_dnload() and __i2400m_dev_start(); they won't become
* multiplicative (__i2400m_dev_start() calling N times
* i2400m_fw_dnload() and this trying N times to download the
* firmware), as if __i2400m_dev_start() only retries if the
* firmware crashed while initializing the device (not in a
* general case).
*
* @bus_bm_cmd_send: [fill] Function called to send a boot-mode * @bus_bm_cmd_send: [fill] Function called to send a boot-mode
* command. Flags are defined in 'enum i2400m_bm_cmd_flags'. This * command. Flags are defined in 'enum i2400m_bm_cmd_flags'. This
* is synchronous and has to return 0 if ok or < 0 errno code in * is synchronous and has to return 0 if ok or < 0 errno code in
...@@ -252,6 +285,12 @@ struct i2400m_roq; ...@@ -252,6 +285,12 @@ struct i2400m_roq;
* address provided in boot mode is kind of broken and needs to * address provided in boot mode is kind of broken and needs to
* be re-read later on. * be re-read later on.
* *
* @bus_bm_pokes_table: [fill/optional] A table of device addresses
* and values that will be poked at device init time to move the
* device to the correct state for the type of boot/firmware being
* used. This table MUST be terminated with (0x000000,
* 0x00000000) or bad things will happen.
*
* *
* @wimax_dev: WiMAX generic device for linkage into the kernel WiMAX * @wimax_dev: WiMAX generic device for linkage into the kernel WiMAX
* stack. Due to the way a net_device is allocated, we need to * stack. Due to the way a net_device is allocated, we need to
...@@ -399,6 +438,8 @@ struct i2400m { ...@@ -399,6 +438,8 @@ struct i2400m {
size_t bus_tx_block_size; size_t bus_tx_block_size;
size_t bus_pl_size_max; size_t bus_pl_size_max;
unsigned bus_bm_retries;
int (*bus_dev_start)(struct i2400m *); int (*bus_dev_start)(struct i2400m *);
void (*bus_dev_stop)(struct i2400m *); void (*bus_dev_stop)(struct i2400m *);
void (*bus_tx_kick)(struct i2400m *); void (*bus_tx_kick)(struct i2400m *);
...@@ -410,6 +451,7 @@ struct i2400m { ...@@ -410,6 +451,7 @@ struct i2400m {
struct i2400m_bootrom_header *, size_t); struct i2400m_bootrom_header *, size_t);
const char **bus_fw_names; const char **bus_fw_names;
unsigned bus_bm_mac_addr_impaired:1; unsigned bus_bm_mac_addr_impaired:1;
const struct i2400m_poke_table *bus_bm_pokes_table;
spinlock_t tx_lock; /* protect TX state */ spinlock_t tx_lock; /* protect TX state */
void *tx_buf; void *tx_buf;
...@@ -709,6 +751,7 @@ static const __le32 i2400m_SBOOT_BARKER[4] = { ...@@ -709,6 +751,7 @@ static const __le32 i2400m_SBOOT_BARKER[4] = {
cpu_to_le32(I2400M_SBOOT_BARKER) cpu_to_le32(I2400M_SBOOT_BARKER)
}; };
extern int i2400m_power_save_disabled;
/* /*
* Utility functions * Utility functions
......
...@@ -54,8 +54,10 @@ int i2400m_radio_is(struct i2400m *i2400m, enum wimax_rf_state state) ...@@ -54,8 +54,10 @@ int i2400m_radio_is(struct i2400m *i2400m, enum wimax_rf_state state)
/* state == WIMAX_RF_ON */ /* state == WIMAX_RF_ON */
return i2400m->state != I2400M_SS_RF_OFF return i2400m->state != I2400M_SS_RF_OFF
&& i2400m->state != I2400M_SS_RF_SHUTDOWN; && i2400m->state != I2400M_SS_RF_SHUTDOWN;
else else {
BUG(); BUG();
return -EINVAL; /* shut gcc warnings on certain arches */
}
} }
......
...@@ -1148,7 +1148,7 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb) ...@@ -1148,7 +1148,7 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb)
num_pls = le16_to_cpu(msg_hdr->num_pls); num_pls = le16_to_cpu(msg_hdr->num_pls);
pl_itr = sizeof(*msg_hdr) + /* Check payload descriptor(s) */ pl_itr = sizeof(*msg_hdr) + /* Check payload descriptor(s) */
num_pls * sizeof(msg_hdr->pld[0]); num_pls * sizeof(msg_hdr->pld[0]);
pl_itr = ALIGN(pl_itr, I2400M_PL_PAD); pl_itr = ALIGN(pl_itr, I2400M_PL_ALIGN);
if (pl_itr > skb->len) { /* got all the payload descriptors? */ if (pl_itr > skb->len) { /* got all the payload descriptors? */
dev_err(dev, "RX: HW BUG? message too short (%u bytes) for " dev_err(dev, "RX: HW BUG? message too short (%u bytes) for "
"%u payload descriptors (%zu each, total %zu)\n", "%u payload descriptors (%zu each, total %zu)\n",
...@@ -1166,7 +1166,7 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb) ...@@ -1166,7 +1166,7 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb)
single_last = num_pls == 1 || i == num_pls - 1; single_last = num_pls == 1 || i == num_pls - 1;
i2400m_rx_payload(i2400m, skb, single_last, &msg_hdr->pld[i], i2400m_rx_payload(i2400m, skb, single_last, &msg_hdr->pld[i],
skb->data + pl_itr); skb->data + pl_itr);
pl_itr += ALIGN(pl_size, I2400M_PL_PAD); pl_itr += ALIGN(pl_size, I2400M_PL_ALIGN);
cond_resched(); /* Don't monopolize */ cond_resched(); /* Don't monopolize */
} }
kfree_skb(skb); kfree_skb(skb);
......
...@@ -46,17 +46,24 @@ ...@@ -46,17 +46,24 @@
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - SDIO rehash for changes in the bus-driver model * - SDIO rehash for changes in the bus-driver model
* *
* Dirk Brandewie <dirk.j.brandewie@intel.com>
* - Make it IRQ based, not polling
*
* THE PROCEDURE * THE PROCEDURE
* *
* See fw.c for the generic description of this procedure. * See fw.c for the generic description of this procedure.
* *
* This file implements only the SDIO specifics. It boils down to how * This file implements only the SDIO specifics. It boils down to how
* to send a command and waiting for an acknowledgement from the * to send a command and waiting for an acknowledgement from the
* device. We do polled reads. * device.
*
* All this code is sequential -- all i2400ms_bus_bm_*() functions are
* executed in the same thread, except i2400ms_bm_irq() [on its own by
* the SDIO driver]. This makes it possible to avoid locking.
* *
* COMMAND EXECUTION * COMMAND EXECUTION
* *
* THe generic firmware upload code will call i2400m_bus_bm_cmd_send() * The generic firmware upload code will call i2400m_bus_bm_cmd_send()
* to send commands. * to send commands.
* *
* The SDIO devices expects things in 256 byte blocks, so it will pad * The SDIO devices expects things in 256 byte blocks, so it will pad
...@@ -64,12 +71,15 @@ ...@@ -64,12 +71,15 @@
* *
* ACK RECEPTION * ACK RECEPTION
* *
* This works in polling mode -- the fw loader says when to wait for * This works in IRQ mode -- the fw loader says when to wait for data
* data and for that it calls i2400ms_bus_bm_wait_for_ack(). * and for that it calls i2400ms_bus_bm_wait_for_ack().
* *
* This will poll the device for data until it is received. We need to * This checks if there is any data available (RX size > 0); if not,
* receive at least as much bytes as where asked for (although it'll * waits for the IRQ handler to notify about it. Once there is data,
* always be a multiple of 256 bytes). * it is read and passed to the caller. Doing it this way we don't
* need much coordination/locking, and it makes it much more difficult
* for an interrupt to be lost and the wait_for_ack() function getting
* stuck even when data is pending.
*/ */
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
#include "i2400m-sdio.h" #include "i2400m-sdio.h"
...@@ -78,6 +88,7 @@ ...@@ -78,6 +88,7 @@
#define D_SUBMODULE fw #define D_SUBMODULE fw
#include "sdio-debug-levels.h" #include "sdio-debug-levels.h"
/* /*
* Send a boot-mode command to the SDIO function * Send a boot-mode command to the SDIO function
* *
...@@ -139,7 +150,7 @@ ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m, ...@@ -139,7 +150,7 @@ ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m,
/* /*
* Read an ack from the device's boot-mode (polling) * Read an ack from the device's boot-mode
* *
* @i2400m: * @i2400m:
* @_ack: pointer to where to store the read data * @_ack: pointer to where to store the read data
...@@ -150,75 +161,49 @@ ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m, ...@@ -150,75 +161,49 @@ ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m,
* The ACK for a BM command is always at least sizeof(*ack) bytes, so * The ACK for a BM command is always at least sizeof(*ack) bytes, so
* check for that. We don't need to check for device reboots * check for that. We don't need to check for device reboots
* *
* NOTE: We do an artificial timeout of 1 sec over the SDIO timeout;
* this way we have control over it...there is no way that I know
* of setting an SDIO transaction timeout.
*/ */
ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
struct i2400m_bootrom_header *ack, struct i2400m_bootrom_header *ack,
size_t ack_size) size_t ack_size)
{ {
int result; ssize_t result;
ssize_t rx_size;
u64 timeout;
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
struct sdio_func *func = i2400ms->func; struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev; struct device *dev = &func->dev;
int size;
BUG_ON(sizeof(*ack) > ack_size); BUG_ON(sizeof(*ack) > ack_size);
d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n", d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n",
i2400m, ack, ack_size); i2400m, ack, ack_size);
timeout = get_jiffies_64() + 2 * HZ; spin_lock(&i2400m->rx_lock);
sdio_claim_host(func); i2400ms->bm_ack_size = -EINPROGRESS;
while (1) { spin_unlock(&i2400m->rx_lock);
if (time_after64(get_jiffies_64(), timeout)) {
rx_size = -ETIMEDOUT;
dev_err(dev, "timeout waiting for ack data\n");
goto error_timedout;
}
/* Find the RX size, check if it fits or not -- it if result = wait_event_timeout(i2400ms->bm_wfa_wq,
* doesn't fit, fail, as we have no way to dispose of i2400ms->bm_ack_size != -EINPROGRESS,
* the extra data. */ 2 * HZ);
rx_size = __i2400ms_rx_get_size(i2400ms); if (result == 0) {
if (rx_size < 0) result = -ETIMEDOUT;
goto error_rx_get_size; dev_err(dev, "BM: error waiting for an ack\n");
result = -ENOSPC; /* Check it fits */ goto error_timeout;
if (rx_size < sizeof(*ack)) { }
rx_size = -EIO;
dev_err(dev, "HW BUG? received is too small (%zu vs "
"%zu needed)\n", sizeof(*ack), rx_size);
goto error_too_small;
}
if (rx_size > I2400M_BM_ACK_BUF_SIZE) {
dev_err(dev, "SW BUG? BM_ACK_BUF is too small (%u vs "
"%zu needed)\n", I2400M_BM_ACK_BUF_SIZE,
rx_size);
goto error_too_small;
}
/* Read it */ spin_lock(&i2400m->rx_lock);
result = sdio_memcpy_fromio(func, i2400m->bm_ack_buf, result = i2400ms->bm_ack_size;
I2400MS_DATA_ADDR, rx_size); BUG_ON(result == -EINPROGRESS);
if (result == -ETIMEDOUT || result == -ETIME) if (result < 0) /* so we exit when rx_release() is called */
continue; dev_err(dev, "BM: %s failed: %zd\n", __func__, result);
if (result < 0) { else {
dev_err(dev, "BM SDIO receive (%zu B) failed: %d\n", size = min(ack_size, i2400ms->bm_ack_size);
rx_size, result); memcpy(ack, i2400m->bm_ack_buf, size);
goto error_read;
} else
break;
} }
rx_size = min((ssize_t)ack_size, rx_size); i2400ms->bm_ack_size = -EINPROGRESS;
memcpy(ack, i2400m->bm_ack_buf, rx_size); spin_unlock(&i2400m->rx_lock);
error_read:
error_too_small: error_timeout:
error_rx_get_size: d_fnend(5, dev, "(i2400m %p ack %p size %zu) = %zd\n",
error_timedout: i2400m, ack, ack_size, result);
sdio_release_host(func); return result;
d_fnend(5, dev, "(i2400m %p ack %p size %zu) = %ld\n",
i2400m, ack, ack_size, (long) rx_size);
return rx_size;
} }
...@@ -69,6 +69,13 @@ ...@@ -69,6 +69,13 @@
#define D_SUBMODULE rx #define D_SUBMODULE rx
#include "sdio-debug-levels.h" #include "sdio-debug-levels.h"
static const __le32 i2400m_ACK_BARKER[4] = {
__constant_cpu_to_le32(I2400M_ACK_BARKER),
__constant_cpu_to_le32(I2400M_ACK_BARKER),
__constant_cpu_to_le32(I2400M_ACK_BARKER),
__constant_cpu_to_le32(I2400M_ACK_BARKER)
};
/* /*
* Read and return the amount of bytes available for RX * Read and return the amount of bytes available for RX
...@@ -131,25 +138,35 @@ void i2400ms_rx(struct i2400ms *i2400ms) ...@@ -131,25 +138,35 @@ void i2400ms_rx(struct i2400ms *i2400ms)
ret = rx_size; ret = rx_size;
goto error_get_size; goto error_get_size;
} }
ret = -ENOMEM; ret = -ENOMEM;
skb = alloc_skb(rx_size, GFP_ATOMIC); skb = alloc_skb(rx_size, GFP_ATOMIC);
if (NULL == skb) { if (NULL == skb) {
dev_err(dev, "RX: unable to alloc skb\n"); dev_err(dev, "RX: unable to alloc skb\n");
goto error_alloc_skb; goto error_alloc_skb;
} }
ret = sdio_memcpy_fromio(func, skb->data, ret = sdio_memcpy_fromio(func, skb->data,
I2400MS_DATA_ADDR, rx_size); I2400MS_DATA_ADDR, rx_size);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "RX: SDIO data read failed: %d\n", ret); dev_err(dev, "RX: SDIO data read failed: %d\n", ret);
goto error_memcpy_fromio; goto error_memcpy_fromio;
} }
/* Check if device has reset */
if (!memcmp(skb->data, i2400m_NBOOT_BARKER, rmb(); /* make sure we get boot_mode from dev_reset_handle */
sizeof(i2400m_NBOOT_BARKER)) if (i2400m->boot_mode == 1) {
|| !memcmp(skb->data, i2400m_SBOOT_BARKER, spin_lock(&i2400m->rx_lock);
sizeof(i2400m_SBOOT_BARKER))) { i2400ms->bm_ack_size = rx_size;
spin_unlock(&i2400m->rx_lock);
memcpy(i2400m->bm_ack_buf, skb->data, rx_size);
wake_up(&i2400ms->bm_wfa_wq);
dev_err(dev, "RX: SDIO boot mode message\n");
kfree_skb(skb);
} else if (unlikely(!memcmp(skb->data, i2400m_NBOOT_BARKER,
sizeof(i2400m_NBOOT_BARKER))
|| !memcmp(skb->data, i2400m_SBOOT_BARKER,
sizeof(i2400m_SBOOT_BARKER)))) {
ret = i2400m_dev_reset_handle(i2400m); ret = i2400m_dev_reset_handle(i2400m);
dev_err(dev, "RX: SDIO reboot barker\n");
kfree_skb(skb); kfree_skb(skb);
} else { } else {
skb_put(skb, rx_size); skb_put(skb, rx_size);
...@@ -179,7 +196,6 @@ void i2400ms_irq(struct sdio_func *func) ...@@ -179,7 +196,6 @@ void i2400ms_irq(struct sdio_func *func)
{ {
int ret; int ret;
struct i2400ms *i2400ms = sdio_get_drvdata(func); struct i2400ms *i2400ms = sdio_get_drvdata(func);
struct i2400m *i2400m = &i2400ms->i2400m;
struct device *dev = &func->dev; struct device *dev = &func->dev;
int val; int val;
...@@ -194,10 +210,7 @@ void i2400ms_irq(struct sdio_func *func) ...@@ -194,10 +210,7 @@ void i2400ms_irq(struct sdio_func *func)
goto error_no_irq; goto error_no_irq;
} }
sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret);
if (WARN_ON(i2400m->boot_mode != 0)) i2400ms_rx(i2400ms);
dev_err(dev, "RX: SW BUG? boot mode and IRQ is up?\n");
else
i2400ms_rx(i2400ms);
error_no_irq: error_no_irq:
d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms);
return; return;
...@@ -214,8 +227,15 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms) ...@@ -214,8 +227,15 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms)
int result; int result;
struct sdio_func *func = i2400ms->func; struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev; struct device *dev = &func->dev;
struct i2400m *i2400m = &i2400ms->i2400m;
d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
init_waitqueue_head(&i2400ms->bm_wfa_wq);
spin_lock(&i2400m->rx_lock);
i2400ms->bm_wait_result = -EINPROGRESS;
spin_unlock(&i2400m->rx_lock);
sdio_claim_host(func); sdio_claim_host(func);
result = sdio_claim_irq(func, i2400ms_irq); result = sdio_claim_irq(func, i2400ms_irq);
if (result < 0) { if (result < 0) {
...@@ -245,8 +265,13 @@ void i2400ms_rx_release(struct i2400ms *i2400ms) ...@@ -245,8 +265,13 @@ void i2400ms_rx_release(struct i2400ms *i2400ms)
int result; int result;
struct sdio_func *func = i2400ms->func; struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev; struct device *dev = &func->dev;
struct i2400m *i2400m = &i2400ms->i2400m;
d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
spin_lock(&i2400m->rx_lock);
i2400ms->bm_ack_size = -EINTR;
spin_unlock(&i2400m->rx_lock);
wake_up_all(&i2400ms->bm_wfa_wq);
sdio_claim_host(func); sdio_claim_host(func);
sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result); sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result);
sdio_release_irq(func); sdio_release_irq(func);
......
...@@ -78,6 +78,14 @@ static const char *i2400ms_bus_fw_names[] = { ...@@ -78,6 +78,14 @@ static const char *i2400ms_bus_fw_names[] = {
}; };
static const struct i2400m_poke_table i2400ms_pokes[] = {
I2400M_FW_POKE(0x6BE260, 0x00000088),
I2400M_FW_POKE(0x080550, 0x00000005),
I2400M_FW_POKE(0xAE0000, 0x00000000),
I2400M_FW_POKE(0x000000, 0x00000000), /* MUST be 0 terminated or bad
* things will happen */
};
/* /*
* Enable the SDIO function * Enable the SDIO function
* *
...@@ -148,19 +156,14 @@ int i2400ms_bus_dev_start(struct i2400m *i2400m) ...@@ -148,19 +156,14 @@ int i2400ms_bus_dev_start(struct i2400m *i2400m)
d_fnstart(3, dev, "(i2400m %p)\n", i2400m); d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
msleep(200); msleep(200);
result = i2400ms_rx_setup(i2400ms);
if (result < 0)
goto error_rx_setup;
result = i2400ms_tx_setup(i2400ms); result = i2400ms_tx_setup(i2400ms);
if (result < 0) if (result < 0)
goto error_tx_setup; goto error_tx_setup;
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result; return result;
i2400ms_tx_release(i2400ms);
error_tx_setup: error_tx_setup:
i2400ms_rx_release(i2400ms); i2400ms_tx_release(i2400ms);
error_rx_setup:
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
return result; return result;
} }
...@@ -174,7 +177,6 @@ void i2400ms_bus_dev_stop(struct i2400m *i2400m) ...@@ -174,7 +177,6 @@ void i2400ms_bus_dev_stop(struct i2400m *i2400m)
struct device *dev = &func->dev; struct device *dev = &func->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m); d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
i2400ms_rx_release(i2400ms);
i2400ms_tx_release(i2400ms); i2400ms_tx_release(i2400ms);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
} }
...@@ -255,7 +257,7 @@ int __i2400ms_send_barker(struct i2400ms *i2400ms, ...@@ -255,7 +257,7 @@ int __i2400ms_send_barker(struct i2400ms *i2400ms,
static static
int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
{ {
int result; int result = 0;
struct i2400ms *i2400ms = struct i2400ms *i2400ms =
container_of(i2400m, struct i2400ms, i2400m); container_of(i2400m, struct i2400ms, i2400m);
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
...@@ -280,8 +282,25 @@ int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) ...@@ -280,8 +282,25 @@ int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
sizeof(i2400m_COLD_BOOT_BARKER)); sizeof(i2400m_COLD_BOOT_BARKER));
else if (rt == I2400M_RT_BUS) { else if (rt == I2400M_RT_BUS) {
do_bus_reset: do_bus_reset:
dev_err(dev, "FIXME: SDIO bus reset not implemented\n"); /* call netif_tx_disable() before sending IOE disable,
result = rt == I2400M_RT_WARM ? -ENODEV : -ENOSYS; * so that all the tx from network layer are stopped
* while IOE is being reset. Make sure it is called
* only after register_netdev() was issued.
*/
if (i2400m->wimax_dev.net_dev->reg_state == NETREG_REGISTERED)
netif_tx_disable(i2400m->wimax_dev.net_dev);
i2400ms_rx_release(i2400ms);
sdio_claim_host(i2400ms->func);
sdio_disable_func(i2400ms->func);
sdio_release_host(i2400ms->func);
/* Wait for the device to settle */
msleep(40);
result = i2400ms_enable_function(i2400ms->func);
if (result >= 0)
i2400ms_rx_setup(i2400ms);
} else } else
BUG(); BUG();
if (result < 0 && rt != I2400M_RT_BUS) { if (result < 0 && rt != I2400M_RT_BUS) {
...@@ -404,10 +423,14 @@ int i2400ms_probe(struct sdio_func *func, ...@@ -404,10 +423,14 @@ int i2400ms_probe(struct sdio_func *func,
i2400m->bus_dev_stop = i2400ms_bus_dev_stop; i2400m->bus_dev_stop = i2400ms_bus_dev_stop;
i2400m->bus_tx_kick = i2400ms_bus_tx_kick; i2400m->bus_tx_kick = i2400ms_bus_tx_kick;
i2400m->bus_reset = i2400ms_bus_reset; i2400m->bus_reset = i2400ms_bus_reset;
/* The iwmc3200-wimax sometimes requires the driver to try
* hard when we paint it into a corner. */
i2400m->bus_bm_retries = I3200_BOOT_RETRIES;
i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send; i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send;
i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack; i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack;
i2400m->bus_fw_names = i2400ms_bus_fw_names; i2400m->bus_fw_names = i2400ms_bus_fw_names;
i2400m->bus_bm_mac_addr_impaired = 1; i2400m->bus_bm_mac_addr_impaired = 1;
i2400m->bus_bm_pokes_table = &i2400ms_pokes[0];
sdio_claim_host(func); sdio_claim_host(func);
result = sdio_set_block_size(func, I2400MS_BLK_SIZE); result = sdio_set_block_size(func, I2400MS_BLK_SIZE);
...@@ -423,6 +446,10 @@ int i2400ms_probe(struct sdio_func *func, ...@@ -423,6 +446,10 @@ int i2400ms_probe(struct sdio_func *func,
goto error_func_enable; goto error_func_enable;
} }
result = i2400ms_rx_setup(i2400ms);
if (result < 0)
goto error_rx_setup;
result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT); result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT);
if (result < 0) { if (result < 0) {
dev_err(dev, "cannot setup device: %d\n", result); dev_err(dev, "cannot setup device: %d\n", result);
...@@ -440,6 +467,8 @@ int i2400ms_probe(struct sdio_func *func, ...@@ -440,6 +467,8 @@ int i2400ms_probe(struct sdio_func *func,
error_debugfs_add: error_debugfs_add:
i2400m_release(i2400m); i2400m_release(i2400m);
error_setup: error_setup:
i2400ms_rx_release(i2400ms);
error_rx_setup:
sdio_claim_host(func); sdio_claim_host(func);
sdio_disable_func(func); sdio_disable_func(func);
sdio_release_host(func); sdio_release_host(func);
...@@ -462,6 +491,7 @@ void i2400ms_remove(struct sdio_func *func) ...@@ -462,6 +491,7 @@ void i2400ms_remove(struct sdio_func *func)
d_fnstart(3, dev, "SDIO func %p\n", func); d_fnstart(3, dev, "SDIO func %p\n", func);
debugfs_remove_recursive(i2400ms->debugfs_dentry); debugfs_remove_recursive(i2400ms->debugfs_dentry);
i2400ms_rx_release(i2400ms);
i2400m_release(i2400m); i2400m_release(i2400m);
sdio_set_drvdata(func, NULL); sdio_set_drvdata(func, NULL);
sdio_claim_host(func); sdio_claim_host(func);
......
...@@ -277,6 +277,48 @@ enum { ...@@ -277,6 +277,48 @@ enum {
#define TAIL_FULL ((void *)~(unsigned long)NULL) #define TAIL_FULL ((void *)~(unsigned long)NULL)
/*
* Calculate how much tail room is available
*
* Note the trick here. This path is ONLY caleed for Case A (see
* i2400m_tx_fifo_push() below), where we have:
*
* Case A
* N ___________
* | tail room |
* | |
* |<- IN ->|
* | |
* | data |
* | |
* |<- OUT ->|
* | |
* | head room |
* 0 -----------
*
* When calculating the tail_room, tx_in might get to be zero if
* i2400m->tx_in is right at the end of the buffer (really full
* buffer) if there is no head room. In this case, tail_room would be
* I2400M_TX_BUF_SIZE, although it is actually zero. Hence the final
* mod (%) operation. However, when doing this kind of optimization,
* i2400m->tx_in being zero would fail, so we treat is an a special
* case.
*/
static inline
size_t __i2400m_tx_tail_room(struct i2400m *i2400m)
{
size_t tail_room;
size_t tx_in;
if (unlikely(i2400m->tx_in) == 0)
return I2400M_TX_BUF_SIZE;
tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE;
tail_room = I2400M_TX_BUF_SIZE - tx_in;
tail_room %= I2400M_TX_BUF_SIZE;
return tail_room;
}
/* /*
* Allocate @size bytes in the TX fifo, return a pointer to it * Allocate @size bytes in the TX fifo, return a pointer to it
* *
...@@ -338,7 +380,7 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) ...@@ -338,7 +380,7 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding)
return NULL; return NULL;
} }
/* Is there space at the tail? */ /* Is there space at the tail? */
tail_room = I2400M_TX_BUF_SIZE - i2400m->tx_in % I2400M_TX_BUF_SIZE; tail_room = __i2400m_tx_tail_room(i2400m);
if (tail_room < needed_size) { if (tail_room < needed_size) {
if (i2400m->tx_out % I2400M_TX_BUF_SIZE if (i2400m->tx_out % I2400M_TX_BUF_SIZE
< i2400m->tx_in % I2400M_TX_BUF_SIZE) { < i2400m->tx_in % I2400M_TX_BUF_SIZE) {
...@@ -367,17 +409,29 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) ...@@ -367,17 +409,29 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding)
* (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the * (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the
* header). * header).
* *
* Tail room can get to be zero if a message was opened when there was
* space only for a header. _tx_close() will mark it as to-skip (as it
* will have no payloads) and there will be no more space to flush, so
* nothing has to be done here. This is probably cheaper than ensuring
* in _tx_new() that there is some space for payloads...as we could
* always possibly hit the same problem if the payload wouldn't fit.
*
* Note: * Note:
* *
* Assumes i2400m->tx_lock is taken, and we use that as a barrier * Assumes i2400m->tx_lock is taken, and we use that as a barrier
*
* This path is only taken for Case A FIFO situations [see
* i2400m_tx_fifo_push()]
*/ */
static static
void i2400m_tx_skip_tail(struct i2400m *i2400m) void i2400m_tx_skip_tail(struct i2400m *i2400m)
{ {
struct device *dev = i2400m_dev(i2400m); struct device *dev = i2400m_dev(i2400m);
size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE;
size_t tail_room = I2400M_TX_BUF_SIZE - tx_in; size_t tail_room = __i2400m_tx_tail_room(i2400m);
struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in; struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in;
if (unlikely(tail_room == 0))
return;
BUG_ON(tail_room < sizeof(*msg)); BUG_ON(tail_room < sizeof(*msg));
msg->size = tail_room | I2400M_TX_SKIP; msg->size = tail_room | I2400M_TX_SKIP;
d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n", d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n",
...@@ -474,10 +528,18 @@ void i2400m_tx_close(struct i2400m *i2400m) ...@@ -474,10 +528,18 @@ void i2400m_tx_close(struct i2400m *i2400m)
struct i2400m_msg_hdr *tx_msg_moved; struct i2400m_msg_hdr *tx_msg_moved;
size_t aligned_size, padding, hdr_size; size_t aligned_size, padding, hdr_size;
void *pad_buf; void *pad_buf;
unsigned num_pls;
if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */ if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */
goto out; goto out;
num_pls = le16_to_cpu(tx_msg->num_pls);
/* We can get this situation when a new message was started
* and there was no space to add payloads before hitting the
tail (and taking padding into consideration). */
if (num_pls == 0) {
tx_msg->size |= I2400M_TX_SKIP;
goto out;
}
/* Relocate the message header /* Relocate the message header
* *
* Find the current header size, align it to 16 and if we need * Find the current header size, align it to 16 and if we need
...@@ -491,7 +553,7 @@ void i2400m_tx_close(struct i2400m *i2400m) ...@@ -491,7 +553,7 @@ void i2400m_tx_close(struct i2400m *i2400m)
*/ */
hdr_size = sizeof(*tx_msg) hdr_size = sizeof(*tx_msg)
+ le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]); + le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]);
hdr_size = ALIGN(hdr_size, I2400M_PL_PAD); hdr_size = ALIGN(hdr_size, I2400M_PL_ALIGN);
tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size; tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size;
tx_msg_moved = (void *) tx_msg + tx_msg->offset; tx_msg_moved = (void *) tx_msg + tx_msg->offset;
memmove(tx_msg_moved, tx_msg, hdr_size); memmove(tx_msg_moved, tx_msg, hdr_size);
...@@ -574,7 +636,7 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, ...@@ -574,7 +636,7 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len,
d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n",
i2400m, buf, buf_len, pl_type); i2400m, buf, buf_len, pl_type);
padded_len = ALIGN(buf_len, I2400M_PL_PAD); padded_len = ALIGN(buf_len, I2400M_PL_ALIGN);
d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len);
/* If there is no current TX message, create one; if the /* If there is no current TX message, create one; if the
* current one is out of payload slots or we have a singleton, * current one is out of payload slots or we have a singleton,
...@@ -591,6 +653,8 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, ...@@ -591,6 +653,8 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len,
i2400m_tx_close(i2400m); i2400m_tx_close(i2400m);
i2400m_tx_new(i2400m); i2400m_tx_new(i2400m);
} }
if (i2400m->tx_msg == NULL)
goto error_tx_new;
if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) { if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) {
d_printf(2, dev, "TX: message too big, going new\n"); d_printf(2, dev, "TX: message too big, going new\n");
i2400m_tx_close(i2400m); i2400m_tx_close(i2400m);
...@@ -773,7 +837,6 @@ void i2400m_tx_msg_sent(struct i2400m *i2400m) ...@@ -773,7 +837,6 @@ void i2400m_tx_msg_sent(struct i2400m *i2400m)
n = i2400m->tx_out / I2400M_TX_BUF_SIZE; n = i2400m->tx_out / I2400M_TX_BUF_SIZE;
i2400m->tx_out %= I2400M_TX_BUF_SIZE; i2400m->tx_out %= I2400M_TX_BUF_SIZE;
i2400m->tx_in -= n * I2400M_TX_BUF_SIZE; i2400m->tx_in -= n * I2400M_TX_BUF_SIZE;
netif_start_queue(i2400m->wimax_dev.net_dev);
spin_unlock_irqrestore(&i2400m->tx_lock, flags); spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
} }
......
...@@ -254,8 +254,10 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) ...@@ -254,8 +254,10 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
dev_err(dev, "USB reset failed (%d), giving up!\n", dev_err(dev, "USB reset failed (%d), giving up!\n",
result); result);
} }
} else } else {
result = -EINVAL; /* shut gcc up in certain arches */
BUG(); BUG();
}
if (result < 0 if (result < 0
&& result != -EINVAL /* device is gone */ && result != -EINVAL /* device is gone */
&& rt != I2400M_RT_BUS) { && rt != I2400M_RT_BUS) {
...@@ -399,6 +401,7 @@ int i2400mu_probe(struct usb_interface *iface, ...@@ -399,6 +401,7 @@ int i2400mu_probe(struct usb_interface *iface,
i2400m->bus_dev_stop = i2400mu_bus_dev_stop; i2400m->bus_dev_stop = i2400mu_bus_dev_stop;
i2400m->bus_tx_kick = i2400mu_bus_tx_kick; i2400m->bus_tx_kick = i2400mu_bus_tx_kick;
i2400m->bus_reset = i2400mu_bus_reset; i2400m->bus_reset = i2400mu_bus_reset;
i2400m->bus_bm_retries = I2400M_BOOT_RETRIES;
i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send;
i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack;
i2400m->bus_fw_names = i2400mu_bus_fw_names; i2400m->bus_fw_names = i2400mu_bus_fw_names;
......
...@@ -266,7 +266,7 @@ enum i2400m_ro_type { ...@@ -266,7 +266,7 @@ enum i2400m_ro_type {
/* Misc constants */ /* Misc constants */
enum { enum {
I2400M_PL_PAD = 16, /* Payload data size alignment */ I2400M_PL_ALIGN = 16, /* Payload data size alignment */
I2400M_PL_SIZE_MAX = 0x3EFF, I2400M_PL_SIZE_MAX = 0x3EFF,
I2400M_MAX_PLS_IN_MSG = 60, I2400M_MAX_PLS_IN_MSG = 60,
/* protocol barkers: sync sequences; for notifications they /* protocol barkers: sync sequences; for notifications they
......
...@@ -113,7 +113,8 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, ...@@ -113,7 +113,8 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
else else
wimax_state = WIMAX_ST_RADIO_OFF; wimax_state = WIMAX_ST_RADIO_OFF;
rfkill_set_hw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF); result = rfkill_set_hw_state(wimax_dev->rfkill,
state == WIMAX_RF_OFF);
__wimax_state_change(wimax_dev, wimax_state); __wimax_state_change(wimax_dev, wimax_state);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment