summaryrefslogtreecommitdiff
path: root/drivers/scsi/bfa/bfa_ioc.c
diff options
context:
space:
mode:
authorKrishna Gudipati <kgudipat@brocade.com>2011-07-20 17:03:46 -0700
committerJames Bottomley <JBottomley@Parallels.com>2011-07-27 14:54:03 +0400
commit45c5dc1d3f42d4f54a5ab5f45ee55f0ffe1099f1 (patch)
treed0a5f76929e47c6876f6db5b9a03a1d60c2d9b62 /drivers/scsi/bfa/bfa_ioc.c
parentc0350bf57445b9e2a4369668127ecc4431472d26 (diff)
[SCSI] bfa: Add support to store driver configuration in flash.
- Added dconf (Driver Config) BFA sub-module. - The dconf sub-module provides interfaces and manages flash writes to the flash DRV parition. - dconf sub-module also ensures that the whole 64K DRV partition is updated on a flash write. Signed-off-by: Krishna Gudipati <kgudipat@brocade.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/bfa/bfa_ioc.c')
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 27c5565ba2c3..1ac5aecf25a6 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -5588,3 +5588,396 @@ bfa_phy_intr(void *phyarg, struct bfi_mbmsg_s *msg)
WARN_ON(1);
}
}
+
+/*
+ * DCONF module specific
+ */
+
+BFA_MODULE(dconf);
+
+/*
+ * DCONF state machine events
+ */
+enum bfa_dconf_event {
+ BFA_DCONF_SM_INIT = 1, /* dconf Init */
+ BFA_DCONF_SM_FLASH_COMP = 2, /* read/write to flash */
+ BFA_DCONF_SM_WR = 3, /* binding change, map */
+ BFA_DCONF_SM_TIMEOUT = 4, /* Start timer */
+ BFA_DCONF_SM_EXIT = 5, /* exit dconf module */
+ BFA_DCONF_SM_IOCDISABLE = 6, /* IOC disable event */
+};
+
+/* forward declaration of DCONF state machine */
+static void bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_dirty(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_sync(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+
+static void bfa_dconf_cbfn(void *dconf, bfa_status_t status);
+static void bfa_dconf_timer(void *cbarg);
+static bfa_status_t bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf);
+static void bfa_dconf_init_cb(void *arg, bfa_status_t status);
+
+/*
+ * Begining state of dconf module. Waiting for an event to start.
+ */
+static void
+bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_status_t bfa_status;
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_INIT:
+ if (dconf->min_cfg) {
+ bfa_trc(dconf->bfa, dconf->min_cfg);
+ return;
+ }
+ bfa_sm_set_state(dconf, bfa_dconf_sm_flash_read);
+ dconf->flashdone = BFA_FALSE;
+ bfa_trc(dconf->bfa, dconf->flashdone);
+ bfa_status = bfa_flash_read_part(BFA_FLASH(dconf->bfa),
+ BFA_FLASH_PART_DRV, dconf->instance,
+ dconf->dconf,
+ sizeof(struct bfa_dconf_s), 0,
+ bfa_dconf_init_cb, dconf->bfa);
+ if (bfa_status != BFA_STATUS_OK) {
+ bfa_dconf_init_cb(dconf->bfa, BFA_STATUS_FAILED);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ return;
+ }
+ break;
+ case BFA_DCONF_SM_EXIT:
+ dconf->flashdone = BFA_TRUE;
+ case BFA_DCONF_SM_IOCDISABLE:
+ case BFA_DCONF_SM_WR:
+ case BFA_DCONF_SM_FLASH_COMP:
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * Read flash for dconf entries and make a call back to the driver once done.
+ */
+static void
+bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_FLASH_COMP:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+ break;
+ case BFA_DCONF_SM_TIMEOUT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ dconf->flashdone = BFA_TRUE;
+ bfa_trc(dconf->bfa, dconf->flashdone);
+ case BFA_DCONF_SM_IOCDISABLE:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * DCONF Module is in ready state. Has completed the initialization.
+ */
+static void
+bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_WR:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ dconf->flashdone = BFA_TRUE;
+ bfa_trc(dconf->bfa, dconf->flashdone);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ break;
+ case BFA_DCONF_SM_INIT:
+ case BFA_DCONF_SM_IOCDISABLE:
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * entries are dirty, write back to the flash.
+ */
+
+static void
+bfa_dconf_sm_dirty(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_TIMEOUT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_sync);
+ bfa_dconf_flash_write(dconf);
+ break;
+ case BFA_DCONF_SM_WR:
+ bfa_timer_stop(&dconf->timer);
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_timer_stop(&dconf->timer);
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_final_sync);
+ bfa_dconf_flash_write(dconf);
+ break;
+ case BFA_DCONF_SM_FLASH_COMP:
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ bfa_timer_stop(&dconf->timer);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_iocdown_dirty);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * Sync the dconf entries to the flash.
+ */
+static void
+bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_IOCDISABLE:
+ case BFA_DCONF_SM_FLASH_COMP:
+ bfa_timer_stop(&dconf->timer);
+ case BFA_DCONF_SM_TIMEOUT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ dconf->flashdone = BFA_TRUE;
+ bfa_trc(dconf->bfa, dconf->flashdone);
+ bfa_ioc_disable(&dconf->bfa->ioc);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+static void
+bfa_dconf_sm_sync(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_FLASH_COMP:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+ break;
+ case BFA_DCONF_SM_WR:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_final_sync);
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_iocdown_dirty);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+static void
+bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_INIT:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ dconf->flashdone = BFA_TRUE;
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * Compute and return memory needed by DRV_CFG module.
+ */
+static void
+bfa_dconf_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo,
+ struct bfa_s *bfa)
+{
+ struct bfa_mem_kva_s *dconf_kva = BFA_MEM_DCONF_KVA(bfa);
+
+ if (cfg->drvcfg.min_cfg)
+ bfa_mem_kva_setup(meminfo, dconf_kva,
+ sizeof(struct bfa_dconf_hdr_s));
+ else
+ bfa_mem_kva_setup(meminfo, dconf_kva,
+ sizeof(struct bfa_dconf_s));
+}
+
+static void
+bfa_dconf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
+ struct bfa_pcidev_s *pcidev)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+
+ dconf->bfad = bfad;
+ dconf->bfa = bfa;
+ dconf->instance = bfa->ioc.port_id;
+ bfa_trc(bfa, dconf->instance);
+
+ dconf->dconf = (struct bfa_dconf_s *) bfa_mem_kva_curp(dconf);
+ if (cfg->drvcfg.min_cfg) {
+ bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_hdr_s);
+ dconf->min_cfg = BFA_TRUE;
+ /*
+ * Set the flashdone flag to TRUE explicitly as no flash
+ * write will happen in min_cfg mode.
+ */
+ dconf->flashdone = BFA_TRUE;
+ } else {
+ dconf->min_cfg = BFA_FALSE;
+ bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_s);
+ }
+
+ bfa_dconf_read_data_valid(bfa) = BFA_FALSE;
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+}
+
+static void
+bfa_dconf_init_cb(void *arg, bfa_status_t status)
+{
+ struct bfa_s *bfa = arg;
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+
+ dconf->flashdone = BFA_TRUE;
+ bfa_trc(bfa, dconf->flashdone);
+ bfa_iocfc_cb_dconf_modinit(bfa, status);
+ if (status == BFA_STATUS_OK) {
+ bfa_dconf_read_data_valid(bfa) = BFA_TRUE;
+ if (dconf->dconf->hdr.signature != BFI_DCONF_SIGNATURE)
+ dconf->dconf->hdr.signature = BFI_DCONF_SIGNATURE;
+ if (dconf->dconf->hdr.version != BFI_DCONF_VERSION)
+ dconf->dconf->hdr.version = BFI_DCONF_VERSION;
+ }
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+}
+
+void
+bfa_dconf_modinit(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_INIT);
+}
+static void
+bfa_dconf_start(struct bfa_s *bfa)
+{
+}
+
+static void
+bfa_dconf_stop(struct bfa_s *bfa)
+{
+}
+
+static void bfa_dconf_timer(void *cbarg)
+{
+ struct bfa_dconf_mod_s *dconf = cbarg;
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_TIMEOUT);
+}
+static void
+bfa_dconf_iocdisable(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_IOCDISABLE);
+}
+
+static void
+bfa_dconf_detach(struct bfa_s *bfa)
+{
+}
+
+static bfa_status_t
+bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf)
+{
+ bfa_status_t bfa_status;
+ bfa_trc(dconf->bfa, 0);
+
+ bfa_status = bfa_flash_update_part(BFA_FLASH(dconf->bfa),
+ BFA_FLASH_PART_DRV, dconf->instance,
+ dconf->dconf, sizeof(struct bfa_dconf_s), 0,
+ bfa_dconf_cbfn, dconf);
+ if (bfa_status != BFA_STATUS_OK)
+ WARN_ON(bfa_status);
+ bfa_trc(dconf->bfa, bfa_status);
+
+ return bfa_status;
+}
+
+bfa_status_t
+bfa_dconf_update(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ bfa_trc(dconf->bfa, 0);
+ if (bfa_sm_cmp_state(dconf, bfa_dconf_sm_iocdown_dirty))
+ return BFA_STATUS_FAILED;
+
+ if (dconf->min_cfg) {
+ bfa_trc(dconf->bfa, dconf->min_cfg);
+ return BFA_STATUS_FAILED;
+ }
+
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_WR);
+ return BFA_STATUS_OK;
+}
+
+static void
+bfa_dconf_cbfn(void *arg, bfa_status_t status)
+{
+ struct bfa_dconf_mod_s *dconf = arg;
+ WARN_ON(status);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+}
+
+void
+bfa_dconf_modexit(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ BFA_DCONF_MOD(bfa)->flashdone = BFA_FALSE;
+ bfa_trc(bfa, BFA_DCONF_MOD(bfa)->flashdone);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_EXIT);
+}